| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <utility> |
| |
| #include "build/build_config.h" |
| |
| #if defined(OS_WIN) |
| #include <windows.h> |
| #include <shlobj.h> |
| #endif |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "base/base64url.h" |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/format_macros.h" |
| #include "base/json/json_reader.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/power_monitor/power_monitor.h" |
| #include "base/power_monitor/power_monitor_source.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/histogram_tester.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "net/base/chunked_upload_data_stream.h" |
| #include "net/base/directory_listing.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/external_estimate_provider.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/load_timing_info.h" |
| #include "net/base/load_timing_info_test_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_module.h" |
| #include "net/base/network_quality_estimator.h" |
| #include "net/base/request_priority.h" |
| #include "net/base/test_data_directory.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/base/upload_data_stream.h" |
| #include "net/base/upload_file_element_reader.h" |
| #include "net/base/url_util.h" |
| #include "net/cert/ct_policy_status.h" |
| #include "net/cert/ct_verifier.h" |
| #include "net/cert/ct_verify_result.h" |
| #include "net/cert/ev_root_ca_metadata.h" |
| #include "net/cert/mock_cert_verifier.h" |
| #include "net/cert/test_root_certs.h" |
| #include "net/cert_net/nss_ocsp.h" |
| #include "net/cookies/cookie_monster.h" |
| #include "net/cookies/cookie_store_test_helpers.h" |
| #include "net/disk_cache/disk_cache.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/http/http_byte_range.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_network_layer.h" |
| #include "net/http/http_network_session.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_util.h" |
| #include "net/log/net_log.h" |
| #include "net/log/test_net_log.h" |
| #include "net/log/test_net_log_entry.h" |
| #include "net/log/test_net_log_util.h" |
| #include "net/proxy/proxy_service.h" |
| #include "net/socket/ssl_client_socket.h" |
| #include "net/ssl/channel_id_service.h" |
| #include "net/ssl/default_channel_id_store.h" |
| #include "net/ssl/ssl_cipher_suite_names.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_server_config.h" |
| #include "net/ssl/token_binding.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/test/spawned_test_server/spawned_test_server.h" |
| #include "net/test/url_request/url_request_failed_job.h" |
| #include "net/url_request/data_protocol_handler.h" |
| #include "net/url_request/static_http_user_agent_settings.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_filter.h" |
| #include "net/url_request/url_request_http_job.h" |
| #include "net/url_request/url_request_intercepting_job_factory.h" |
| #include "net/url_request/url_request_interceptor.h" |
| #include "net/url_request/url_request_job_factory_impl.h" |
| #include "net/url_request/url_request_redirect_job.h" |
| #include "net/url_request/url_request_test_job.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/platform_test.h" |
| |
| #if !defined(DISABLE_FILE_SUPPORT) |
| #include "net/base/filename_util.h" |
| #include "net/url_request/file_protocol_handler.h" |
| #include "net/url_request/url_request_file_dir_job.h" |
| #endif |
| |
| #if !defined(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) |
| #include "net/ftp/ftp_network_layer.h" |
| #include "net/url_request/ftp_protocol_handler.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/win/scoped_com_initializer.h" |
| #include "base/win/scoped_comptr.h" |
| #include "base/win/windows_version.h" |
| #endif |
| |
| using base::ASCIIToUTF16; |
| using base::Time; |
| using std::string; |
| |
| namespace net { |
| |
| namespace { |
| |
| const base::string16 kChrome(ASCIIToUTF16("chrome")); |
| const base::string16 kSecret(ASCIIToUTF16("secret")); |
| const base::string16 kUser(ASCIIToUTF16("user")); |
| |
| const base::FilePath::CharType kTestFilePath[] = |
| FILE_PATH_LITERAL("net/data/url_request_unittest"); |
| |
| #if !defined(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) |
| // Test file used in most FTP tests. |
| const char kFtpTestFile[] = "BullRunSpeech.txt"; |
| #endif |
| |
| // Tests load timing information in the case a fresh connection was used, with |
| // no proxy. |
| void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info, |
| int connect_timing_flags) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| EXPECT_LE(load_timing_info.request_start, |
| load_timing_info.connect_timing.connect_start); |
| ExpectConnectTimingHasTimes(load_timing_info.connect_timing, |
| connect_timing_flags); |
| EXPECT_LE(load_timing_info.connect_timing.connect_end, |
| load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); |
| |
| EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); |
| EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); |
| } |
| |
| // Same as above, but with proxy times. |
| void TestLoadTimingNotReusedWithProxy( |
| const LoadTimingInfo& load_timing_info, |
| int connect_timing_flags) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| EXPECT_LE(load_timing_info.request_start, |
| load_timing_info.proxy_resolve_start); |
| EXPECT_LE(load_timing_info.proxy_resolve_start, |
| load_timing_info.proxy_resolve_end); |
| EXPECT_LE(load_timing_info.proxy_resolve_end, |
| load_timing_info.connect_timing.connect_start); |
| ExpectConnectTimingHasTimes(load_timing_info.connect_timing, |
| connect_timing_flags); |
| EXPECT_LE(load_timing_info.connect_timing.connect_end, |
| load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); |
| } |
| |
| // Same as above, but with a reused socket and proxy times. |
| void TestLoadTimingReusedWithProxy( |
| const LoadTimingInfo& load_timing_info) { |
| EXPECT_TRUE(load_timing_info.socket_reused); |
| EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); |
| |
| EXPECT_LE(load_timing_info.request_start, |
| load_timing_info.proxy_resolve_start); |
| EXPECT_LE(load_timing_info.proxy_resolve_start, |
| load_timing_info.proxy_resolve_end); |
| EXPECT_LE(load_timing_info.proxy_resolve_end, |
| load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); |
| } |
| |
| #if !defined(DISABLE_FILE_SUPPORT) |
| // Tests load timing information in the case of a cache hit, when no cache |
| // validation request was sent over the wire. |
| base::StringPiece TestNetResourceProvider(int key) { |
| return "header"; |
| } |
| |
| void FillBuffer(char* buffer, size_t len) { |
| static bool called = false; |
| if (!called) { |
| called = true; |
| int seed = static_cast<int>(Time::Now().ToInternalValue()); |
| srand(seed); |
| } |
| |
| for (size_t i = 0; i < len; i++) { |
| buffer[i] = static_cast<char>(rand()); |
| if (!buffer[i]) |
| buffer[i] = 'g'; |
| } |
| } |
| #endif |
| |
| void TestLoadTimingCacheHitNoNetwork( |
| const LoadTimingInfo& load_timing_info) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_EQ(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); |
| EXPECT_LE(load_timing_info.request_start, load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); |
| |
| EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); |
| EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); |
| } |
| |
| #if !defined(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) |
| // Tests load timing in the case that there is no HTTP response. This can be |
| // used to test in the case of errors or non-HTTP requests. |
| void TestLoadTimingNoHttpResponse( |
| const LoadTimingInfo& load_timing_info) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_EQ(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); |
| |
| // Only the request times should be non-null. |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); |
| |
| EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); |
| EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); |
| EXPECT_TRUE(load_timing_info.send_start.is_null()); |
| EXPECT_TRUE(load_timing_info.send_end.is_null()); |
| EXPECT_TRUE(load_timing_info.receive_headers_end.is_null()); |
| } |
| #endif |
| |
| // Test power monitor source that can simulate entering suspend mode. Can't use |
| // the one in base/ because it insists on bringing its own MessageLoop. |
| class TestPowerMonitorSource : public base::PowerMonitorSource { |
| public: |
| TestPowerMonitorSource() {} |
| ~TestPowerMonitorSource() override {} |
| |
| void Suspend() { ProcessPowerEvent(SUSPEND_EVENT); } |
| |
| void Resume() { ProcessPowerEvent(RESUME_EVENT); } |
| |
| bool IsOnBatteryPowerImpl() override { return false; } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestPowerMonitorSource); |
| }; |
| |
| // Job that allows monitoring of its priority. |
| class PriorityMonitoringURLRequestJob : public URLRequestTestJob { |
| public: |
| // The latest priority of the job is always written to |request_priority_|. |
| PriorityMonitoringURLRequestJob(URLRequest* request, |
| NetworkDelegate* network_delegate, |
| RequestPriority* request_priority) |
| : URLRequestTestJob(request, network_delegate), |
| request_priority_(request_priority) { |
| *request_priority_ = DEFAULT_PRIORITY; |
| } |
| |
| void SetPriority(RequestPriority priority) override { |
| *request_priority_ = priority; |
| URLRequestTestJob::SetPriority(priority); |
| } |
| |
| private: |
| RequestPriority* const request_priority_; |
| }; |
| |
| // Do a case-insensitive search through |haystack| for |needle|. |
| bool ContainsString(const std::string& haystack, const char* needle) { |
| std::string::const_iterator it = std::search( |
| haystack.begin(), haystack.end(), needle, needle + strlen(needle), |
| base::CaseInsensitiveCompareASCII<char>()); |
| return it != haystack.end(); |
| } |
| |
| scoped_ptr<UploadDataStream> CreateSimpleUploadData(const char* data) { |
| scoped_ptr<UploadElementReader> reader( |
| new UploadBytesElementReader(data, strlen(data))); |
| return ElementsUploadDataStream::CreateWithReader(std::move(reader), 0); |
| } |
| |
| // Verify that the SSLInfo of a successful SSL connection has valid values. |
| void CheckSSLInfo(const SSLInfo& ssl_info) { |
| // -1 means unknown. 0 means no encryption. |
| EXPECT_GT(ssl_info.security_bits, 0); |
| |
| // The cipher suite TLS_NULL_WITH_NULL_NULL (0) must not be negotiated. |
| uint16_t cipher_suite = |
| SSLConnectionStatusToCipherSuite(ssl_info.connection_status); |
| EXPECT_NE(0U, cipher_suite); |
| } |
| |
| void CheckFullRequestHeaders(const HttpRequestHeaders& headers, |
| const GURL& host_url) { |
| std::string sent_value; |
| |
| EXPECT_TRUE(headers.GetHeader("Host", &sent_value)); |
| EXPECT_EQ(GetHostAndOptionalPort(host_url), sent_value); |
| |
| EXPECT_TRUE(headers.GetHeader("Connection", &sent_value)); |
| EXPECT_EQ("keep-alive", sent_value); |
| } |
| |
| #if !defined(OS_IOS) |
| bool FingerprintsEqual(const HashValueVector& a, const HashValueVector& b) { |
| size_t size = a.size(); |
| |
| if (size != b.size()) |
| return false; |
| |
| for (size_t i = 0; i < size; ++i) { |
| if (!a[i].Equals(b[i])) |
| return false; |
| } |
| |
| return true; |
| } |
| #endif // !defined(OS_IOS) |
| |
| // A network delegate that allows the user to choose a subset of request stages |
| // to block in. When blocking, the delegate can do one of the following: |
| // * synchronously return a pre-specified error code, or |
| // * asynchronously return that value via an automatically called callback, |
| // or |
| // * block and wait for the user to do a callback. |
| // Additionally, the user may also specify a redirect URL -- then each request |
| // with the current URL different from the redirect target will be redirected |
| // to that target, in the on-before-URL-request stage, independent of whether |
| // the delegate blocks in ON_BEFORE_URL_REQUEST or not. |
| class BlockingNetworkDelegate : public TestNetworkDelegate { |
| public: |
| // Stages in which the delegate can block. |
| enum Stage { |
| NOT_BLOCKED = 0, |
| ON_BEFORE_URL_REQUEST = 1 << 0, |
| ON_BEFORE_SEND_HEADERS = 1 << 1, |
| ON_HEADERS_RECEIVED = 1 << 2, |
| ON_AUTH_REQUIRED = 1 << 3 |
| }; |
| |
| // Behavior during blocked stages. During other stages, just |
| // returns OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION. |
| enum BlockMode { |
| SYNCHRONOUS, // No callback, returns specified return values. |
| AUTO_CALLBACK, // |this| posts a task to run the callback using the |
| // specified return codes. |
| USER_CALLBACK, // User takes care of doing a callback. |retval_| and |
| // |auth_retval_| are ignored. In every blocking stage the |
| // message loop is quit. |
| }; |
| |
| // Creates a delegate which does not block at all. |
| explicit BlockingNetworkDelegate(BlockMode block_mode); |
| |
| // For users to trigger a callback returning |response|. |
| // Side-effects: resets |stage_blocked_for_callback_| and stored callbacks. |
| // Only call if |block_mode_| == USER_CALLBACK. |
| void DoCallback(int response); |
| void DoAuthCallback(NetworkDelegate::AuthRequiredResponse response); |
| |
| // Setters. |
| void set_retval(int retval) { |
| ASSERT_NE(USER_CALLBACK, block_mode_); |
| ASSERT_NE(ERR_IO_PENDING, retval); |
| ASSERT_NE(OK, retval); |
| retval_ = retval; |
| } |
| |
| // If |auth_retval| == AUTH_REQUIRED_RESPONSE_SET_AUTH, then |
| // |auth_credentials_| will be passed with the response. |
| void set_auth_retval(AuthRequiredResponse auth_retval) { |
| ASSERT_NE(USER_CALLBACK, block_mode_); |
| ASSERT_NE(AUTH_REQUIRED_RESPONSE_IO_PENDING, auth_retval); |
| auth_retval_ = auth_retval; |
| } |
| void set_auth_credentials(const AuthCredentials& auth_credentials) { |
| auth_credentials_ = auth_credentials; |
| } |
| |
| void set_redirect_url(const GURL& url) { |
| redirect_url_ = url; |
| } |
| |
| void set_block_on(int block_on) { |
| block_on_ = block_on; |
| } |
| |
| // Allows the user to check in which state did we block. |
| Stage stage_blocked_for_callback() const { |
| EXPECT_EQ(USER_CALLBACK, block_mode_); |
| return stage_blocked_for_callback_; |
| } |
| |
| private: |
| void RunCallback(int response, const CompletionCallback& callback); |
| void RunAuthCallback(AuthRequiredResponse response, |
| const AuthCallback& callback); |
| |
| // TestNetworkDelegate implementation. |
| int OnBeforeURLRequest(URLRequest* request, |
| const CompletionCallback& callback, |
| GURL* new_url) override; |
| |
| int OnBeforeSendHeaders(URLRequest* request, |
| const CompletionCallback& callback, |
| HttpRequestHeaders* headers) override; |
| |
| int OnHeadersReceived( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| GURL* allowed_unsafe_redirect_url) override; |
| |
| NetworkDelegate::AuthRequiredResponse OnAuthRequired( |
| URLRequest* request, |
| const AuthChallengeInfo& auth_info, |
| const AuthCallback& callback, |
| AuthCredentials* credentials) override; |
| |
| // Resets the callbacks and |stage_blocked_for_callback_|. |
| void Reset(); |
| |
| // Checks whether we should block in |stage|. If yes, returns an error code |
| // and optionally sets up callback based on |block_mode_|. If no, returns OK. |
| int MaybeBlockStage(Stage stage, const CompletionCallback& callback); |
| |
| // Configuration parameters, can be adjusted by public methods: |
| const BlockMode block_mode_; |
| |
| // Values returned on blocking stages when mode is SYNCHRONOUS or |
| // AUTO_CALLBACK. For USER_CALLBACK these are set automatically to IO_PENDING. |
| int retval_; // To be returned in non-auth stages. |
| AuthRequiredResponse auth_retval_; |
| |
| GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest. |
| int block_on_; // Bit mask: in which stages to block. |
| |
| // |auth_credentials_| will be copied to |*target_auth_credential_| on |
| // callback. |
| AuthCredentials auth_credentials_; |
| AuthCredentials* target_auth_credentials_; |
| |
| // Internal variables, not set by not the user: |
| // Last blocked stage waiting for user callback (unused if |block_mode_| != |
| // USER_CALLBACK). |
| Stage stage_blocked_for_callback_; |
| |
| // Callback objects stored during blocking stages. |
| CompletionCallback callback_; |
| AuthCallback auth_callback_; |
| |
| base::WeakPtrFactory<BlockingNetworkDelegate> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BlockingNetworkDelegate); |
| }; |
| |
| BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode) |
| : block_mode_(block_mode), |
| retval_(OK), |
| auth_retval_(AUTH_REQUIRED_RESPONSE_NO_ACTION), |
| block_on_(0), |
| target_auth_credentials_(NULL), |
| stage_blocked_for_callback_(NOT_BLOCKED), |
| weak_factory_(this) { |
| } |
| |
| void BlockingNetworkDelegate::DoCallback(int response) { |
| ASSERT_EQ(USER_CALLBACK, block_mode_); |
| ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_); |
| ASSERT_NE(ON_AUTH_REQUIRED, stage_blocked_for_callback_); |
| CompletionCallback callback = callback_; |
| Reset(); |
| RunCallback(response, callback); |
| } |
| |
| void BlockingNetworkDelegate::DoAuthCallback( |
| NetworkDelegate::AuthRequiredResponse response) { |
| ASSERT_EQ(USER_CALLBACK, block_mode_); |
| ASSERT_EQ(ON_AUTH_REQUIRED, stage_blocked_for_callback_); |
| AuthCallback auth_callback = auth_callback_; |
| Reset(); |
| RunAuthCallback(response, auth_callback); |
| } |
| |
| void BlockingNetworkDelegate::RunCallback(int response, |
| const CompletionCallback& callback) { |
| callback.Run(response); |
| } |
| |
| void BlockingNetworkDelegate::RunAuthCallback(AuthRequiredResponse response, |
| const AuthCallback& callback) { |
| if (auth_retval_ == AUTH_REQUIRED_RESPONSE_SET_AUTH) { |
| ASSERT_TRUE(target_auth_credentials_ != NULL); |
| *target_auth_credentials_ = auth_credentials_; |
| } |
| callback.Run(response); |
| } |
| |
| int BlockingNetworkDelegate::OnBeforeURLRequest( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| GURL* new_url) { |
| if (redirect_url_ == request->url()) |
| return OK; // We've already seen this request and redirected elsewhere. |
| |
| TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url); |
| |
| if (!redirect_url_.is_empty()) |
| *new_url = redirect_url_; |
| |
| return MaybeBlockStage(ON_BEFORE_URL_REQUEST, callback); |
| } |
| |
| int BlockingNetworkDelegate::OnBeforeSendHeaders( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| HttpRequestHeaders* headers) { |
| TestNetworkDelegate::OnBeforeSendHeaders(request, callback, headers); |
| |
| return MaybeBlockStage(ON_BEFORE_SEND_HEADERS, callback); |
| } |
| |
| int BlockingNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| GURL* allowed_unsafe_redirect_url) { |
| TestNetworkDelegate::OnHeadersReceived(request, |
| callback, |
| original_response_headers, |
| override_response_headers, |
| allowed_unsafe_redirect_url); |
| |
| return MaybeBlockStage(ON_HEADERS_RECEIVED, callback); |
| } |
| |
| NetworkDelegate::AuthRequiredResponse BlockingNetworkDelegate::OnAuthRequired( |
| URLRequest* request, |
| const AuthChallengeInfo& auth_info, |
| const AuthCallback& callback, |
| AuthCredentials* credentials) { |
| TestNetworkDelegate::OnAuthRequired(request, auth_info, callback, |
| credentials); |
| // Check that the user has provided callback for the previous blocked stage. |
| EXPECT_EQ(NOT_BLOCKED, stage_blocked_for_callback_); |
| |
| if ((block_on_ & ON_AUTH_REQUIRED) == 0) { |
| return AUTH_REQUIRED_RESPONSE_NO_ACTION; |
| } |
| |
| target_auth_credentials_ = credentials; |
| |
| switch (block_mode_) { |
| case SYNCHRONOUS: |
| if (auth_retval_ == AUTH_REQUIRED_RESPONSE_SET_AUTH) |
| *target_auth_credentials_ = auth_credentials_; |
| return auth_retval_; |
| |
| case AUTO_CALLBACK: |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&BlockingNetworkDelegate::RunAuthCallback, |
| weak_factory_.GetWeakPtr(), auth_retval_, callback)); |
| return AUTH_REQUIRED_RESPONSE_IO_PENDING; |
| |
| case USER_CALLBACK: |
| auth_callback_ = callback; |
| stage_blocked_for_callback_ = ON_AUTH_REQUIRED; |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); |
| return AUTH_REQUIRED_RESPONSE_IO_PENDING; |
| } |
| NOTREACHED(); |
| return AUTH_REQUIRED_RESPONSE_NO_ACTION; // Dummy value. |
| } |
| |
| void BlockingNetworkDelegate::Reset() { |
| EXPECT_NE(NOT_BLOCKED, stage_blocked_for_callback_); |
| stage_blocked_for_callback_ = NOT_BLOCKED; |
| callback_.Reset(); |
| auth_callback_.Reset(); |
| } |
| |
| int BlockingNetworkDelegate::MaybeBlockStage( |
| BlockingNetworkDelegate::Stage stage, |
| const CompletionCallback& callback) { |
| // Check that the user has provided callback for the previous blocked stage. |
| EXPECT_EQ(NOT_BLOCKED, stage_blocked_for_callback_); |
| |
| if ((block_on_ & stage) == 0) { |
| return OK; |
| } |
| |
| switch (block_mode_) { |
| case SYNCHRONOUS: |
| EXPECT_NE(OK, retval_); |
| return retval_; |
| |
| case AUTO_CALLBACK: |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::Bind(&BlockingNetworkDelegate::RunCallback, |
| weak_factory_.GetWeakPtr(), retval_, callback)); |
| return ERR_IO_PENDING; |
| |
| case USER_CALLBACK: |
| callback_ = callback; |
| stage_blocked_for_callback_ = stage; |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); |
| return ERR_IO_PENDING; |
| } |
| NOTREACHED(); |
| return 0; |
| } |
| |
| class TestURLRequestContextWithProxy : public TestURLRequestContext { |
| public: |
| // Does not own |delegate|. |
| TestURLRequestContextWithProxy(const std::string& proxy, |
| NetworkDelegate* delegate) |
| : TestURLRequestContext(true) { |
| context_storage_.set_proxy_service(ProxyService::CreateFixed(proxy)); |
| set_network_delegate(delegate); |
| Init(); |
| } |
| ~TestURLRequestContextWithProxy() override {} |
| }; |
| |
| // A mock ReportSender that just remembers the latest report |
| // URI and report to be sent. |
| class MockCertificateReportSender |
| : public TransportSecurityState::ReportSender { |
| public: |
| MockCertificateReportSender() {} |
| ~MockCertificateReportSender() override {} |
| |
| void Send(const GURL& report_uri, const std::string& report) override { |
| latest_report_uri_ = report_uri; |
| latest_report_ = report; |
| } |
| |
| const GURL& latest_report_uri() { return latest_report_uri_; } |
| const std::string& latest_report() { return latest_report_; } |
| |
| private: |
| GURL latest_report_uri_; |
| std::string latest_report_; |
| }; |
| |
| class TestExperimentalFeaturesNetworkDelegate : public TestNetworkDelegate { |
| public: |
| bool OnAreExperimentalCookieFeaturesEnabled() const override { return true; } |
| bool OnAreStrictSecureCookiesEnabled() const override { return true; } |
| }; |
| |
| } // namespace |
| |
| // Inherit PlatformTest since we require the autorelease pool on Mac OS X. |
| class URLRequestTest : public PlatformTest { |
| public: |
| URLRequestTest() : default_context_(true) { |
| default_context_.set_network_delegate(&default_network_delegate_); |
| default_context_.set_net_log(&net_log_); |
| job_factory_impl_ = new URLRequestJobFactoryImpl(); |
| job_factory_.reset(job_factory_impl_); |
| } |
| |
| ~URLRequestTest() override { |
| // URLRequestJobs may post clean-up tasks on destruction. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetUp() override { |
| SetUpFactory(); |
| default_context_.set_job_factory(job_factory_.get()); |
| default_context_.Init(); |
| PlatformTest::SetUp(); |
| } |
| |
| virtual void SetUpFactory() { |
| job_factory_impl_->SetProtocolHandler( |
| "data", make_scoped_ptr(new DataProtocolHandler)); |
| #if !defined(DISABLE_FILE_SUPPORT) |
| job_factory_impl_->SetProtocolHandler( |
| "file", make_scoped_ptr(new FileProtocolHandler( |
| base::ThreadTaskRunnerHandle::Get()))); |
| #endif |
| } |
| |
| TestNetworkDelegate* default_network_delegate() { |
| return &default_network_delegate_; |
| } |
| |
| const TestURLRequestContext& default_context() const { |
| return default_context_; |
| } |
| |
| |
| // Adds the TestJobInterceptor to the default context. |
| TestJobInterceptor* AddTestInterceptor() { |
| TestJobInterceptor* protocol_handler_ = new TestJobInterceptor(); |
| job_factory_impl_->SetProtocolHandler("http", nullptr); |
| job_factory_impl_->SetProtocolHandler("http", |
| make_scoped_ptr(protocol_handler_)); |
| return protocol_handler_; |
| } |
| |
| protected: |
| TestNetLog net_log_; |
| TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. |
| URLRequestJobFactoryImpl* job_factory_impl_; |
| scoped_ptr<URLRequestJobFactory> job_factory_; |
| TestURLRequestContext default_context_; |
| }; |
| |
| TEST_F(URLRequestTest, AboutBlankTest) { |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| GURL("about:blank"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(d.bytes_received(), 0); |
| EXPECT_EQ("", r->GetSocketAddress().host()); |
| EXPECT_EQ(0, r->GetSocketAddress().port()); |
| |
| HttpRequestHeaders headers; |
| EXPECT_FALSE(r->GetFullRequestHeaders(&headers)); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DataURLImageTest) { |
| TestDelegate d; |
| { |
| // Use our nice little Chrome logo. |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| GURL( |
| "data:image/png;base64," |
| "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADVklEQVQ4jX2TfUwUB" |
| "BjG3w1y+HGcd9dxhXR8T4awOccJGgOSWclHImznLkTlSw0DDQXkrmgYgbUYnlQTqQ" |
| "xIEVxitD5UMCATRA1CEEg+Qjw3bWDxIauJv/5oumqs39/P827vnucRmYN0gyF01GI" |
| "5MpCVdW0gO7tvNC+vqSEtbZefk5NuLv1jdJ46p/zw0HeH4+PHr3h7c1mjoV2t5rKz" |
| "Mx1+fg9bAgK6zHq9cU5z+LpA3xOtx34+vTeT21onRuzssC3zxbbSwC13d/pFuC7Ck" |
| "IMDxQpF7r/MWq12UctI1dWWm99ypqSYmRUBdKem8MkrO/kgaTt1O7YzlpzE5GIVd0" |
| "WYUqt57yWf2McHTObYPbVD+ZwbtlLTVMZ3BW+TnLyXLaWtmEq6WJVbT3HBh3Svj2H" |
| "QQcm43XwmtoYM6vVKleh0uoWvnzW3v3MpidruPTQPf0bia7sJOtBM0ufTWNvus/nk" |
| "DFHF9ZS+uYVjRUasMeHUmyLYtcklTvzWGFZnNOXczThvpKIzjcahSqIzkvDLayDq6" |
| "D3eOjtBbNUEIZYyqsvj4V4wY92eNJ4IoyhTbxXX1T5xsV9tm9r4TQwHLiZw/pdDZJ" |
| "ea8TKmsmR/K0uLh/GwnCHghTja6lPhphezPfO5/5MrVvMzNaI3+ERHfrFzPKQukrQ" |
| "GI4d/3EFD/3E2mVNYvi4at7CXWREaxZGD+3hg28zD3gVMd6q5c8GdosynKmSeRuGz" |
| "pjyl1/9UDGtPR5HeaKT8Wjo17WXk579BXVUhN64ehF9fhRtq/uxxZKzNiZFGD0wRC" |
| "3NFROZ5mwIPL/96K/rKMMLrIzF9uhHr+/sYH7DAbwlgC4J+R2Z7FUx1qLnV7MGF40" |
| "smVSoJ/jvHRfYhQeUJd/SnYtGWhPHR0Sz+GE2F2yth0B36Vcz2KpnufBJbsysjjW4" |
| "kblBUiIjiURUWqJY65zxbnTy57GQyH58zgy0QBtTQv5gH15XMdKkYu+TGaJMnlm2O" |
| "34uI4b9tflqp1+QEFGzoW/ulmcofcpkZCYJhDfSpme7QcrHa+Xfji8paEQkTkSfmm" |
| "oRWRNZr/F1KfVMjW+IKEnv2FwZfKdzt0BQR6lClcZR0EfEXEfv/G6W9iLiIyCoReV" |
| "5EnhORIBHx+ufPj/gLB/zGI/G4Bk0AAAAASUVORK5CYII="), |
| DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(d.bytes_received(), 911); |
| EXPECT_EQ("", r->GetSocketAddress().host()); |
| EXPECT_EQ(0, r->GetSocketAddress().port()); |
| |
| HttpRequestHeaders headers; |
| EXPECT_FALSE(r->GetFullRequestHeaders(&headers)); |
| } |
| } |
| |
| #if !defined(DISABLE_FILE_SUPPORT) |
| TEST_F(URLRequestTest, FileTest) { |
| base::FilePath app_path; |
| PathService::Get(base::FILE_EXE, &app_path); |
| GURL app_url = FilePathToFileURL(app_path); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(app_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| int64_t file_size = -1; |
| EXPECT_TRUE(base::GetFileSize(app_path, &file_size)); |
| |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(d.bytes_received(), static_cast<int>(file_size)); |
| EXPECT_EQ("", r->GetSocketAddress().host()); |
| EXPECT_EQ(0, r->GetSocketAddress().port()); |
| |
| HttpRequestHeaders headers; |
| EXPECT_FALSE(r->GetFullRequestHeaders(&headers)); |
| } |
| } |
| |
| TEST_F(URLRequestTest, FileTestCancel) { |
| base::FilePath app_path; |
| PathService::Get(base::FILE_EXE, &app_path); |
| GURL app_url = FilePathToFileURL(app_path); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(app_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| r->Cancel(); |
| } |
| // Async cancellation should be safe even when URLRequest has been already |
| // destroyed. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(URLRequestTest, FileTestFullSpecifiedRange) { |
| const size_t buffer_size = 4000; |
| scoped_ptr<char[]> buffer(new char[buffer_size]); |
| FillBuffer(buffer.get(), buffer_size); |
| |
| base::FilePath temp_path; |
| EXPECT_TRUE(base::CreateTemporaryFile(&temp_path)); |
| GURL temp_url = FilePathToFileURL(temp_path); |
| EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size)); |
| |
| int64_t file_size; |
| EXPECT_TRUE(base::GetFileSize(temp_path, &file_size)); |
| |
| const size_t first_byte_position = 500; |
| const size_t last_byte_position = buffer_size - first_byte_position; |
| const size_t content_length = last_byte_position - first_byte_position + 1; |
| std::string partial_buffer_string(buffer.get() + first_byte_position, |
| buffer.get() + last_byte_position + 1); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(temp_url, DEFAULT_PRIORITY, &d)); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader( |
| HttpRequestHeaders::kRange, |
| HttpByteRange::Bounded( |
| first_byte_position, last_byte_position).GetHeaderValue()); |
| r->SetExtraRequestHeaders(headers); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(static_cast<int>(content_length), d.bytes_received()); |
| // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed. |
| EXPECT_TRUE(partial_buffer_string == d.data_received()); |
| } |
| |
| EXPECT_TRUE(base::DeleteFile(temp_path, false)); |
| } |
| |
| TEST_F(URLRequestTest, FileTestHalfSpecifiedRange) { |
| const size_t buffer_size = 4000; |
| scoped_ptr<char[]> buffer(new char[buffer_size]); |
| FillBuffer(buffer.get(), buffer_size); |
| |
| base::FilePath temp_path; |
| EXPECT_TRUE(base::CreateTemporaryFile(&temp_path)); |
| GURL temp_url = FilePathToFileURL(temp_path); |
| EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size)); |
| |
| int64_t file_size; |
| EXPECT_TRUE(base::GetFileSize(temp_path, &file_size)); |
| |
| const size_t first_byte_position = 500; |
| const size_t last_byte_position = buffer_size - 1; |
| const size_t content_length = last_byte_position - first_byte_position + 1; |
| std::string partial_buffer_string(buffer.get() + first_byte_position, |
| buffer.get() + last_byte_position + 1); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(temp_url, DEFAULT_PRIORITY, &d)); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kRange, |
| HttpByteRange::RightUnbounded( |
| first_byte_position).GetHeaderValue()); |
| r->SetExtraRequestHeaders(headers); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(static_cast<int>(content_length), d.bytes_received()); |
| // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed. |
| EXPECT_TRUE(partial_buffer_string == d.data_received()); |
| } |
| |
| EXPECT_TRUE(base::DeleteFile(temp_path, false)); |
| } |
| |
| TEST_F(URLRequestTest, FileTestMultipleRanges) { |
| const size_t buffer_size = 400000; |
| scoped_ptr<char[]> buffer(new char[buffer_size]); |
| FillBuffer(buffer.get(), buffer_size); |
| |
| base::FilePath temp_path; |
| EXPECT_TRUE(base::CreateTemporaryFile(&temp_path)); |
| GURL temp_url = FilePathToFileURL(temp_path); |
| EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size)); |
| |
| int64_t file_size; |
| EXPECT_TRUE(base::GetFileSize(temp_path, &file_size)); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(temp_url, DEFAULT_PRIORITY, &d)); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kRange, "bytes=0-0,10-200,200-300"); |
| r->SetExtraRequestHeaders(headers); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| EXPECT_TRUE(d.request_failed()); |
| } |
| |
| EXPECT_TRUE(base::DeleteFile(temp_path, false)); |
| } |
| |
| TEST_F(URLRequestTest, AllowFileURLs) { |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath test_file; |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &test_file)); |
| std::string test_data("monkey"); |
| base::WriteFile(test_file, test_data.data(), test_data.size()); |
| GURL test_file_url = FilePathToFileURL(test_file); |
| |
| { |
| TestDelegate d; |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_can_access_files(true); |
| default_context_.set_network_delegate(&network_delegate); |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(test_file_url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_FALSE(d.request_failed()); |
| EXPECT_EQ(test_data, d.data_received()); |
| } |
| |
| { |
| TestDelegate d; |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_can_access_files(false); |
| default_context_.set_network_delegate(&network_delegate); |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(test_file_url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_EQ("", d.data_received()); |
| } |
| } |
| |
| |
| TEST_F(URLRequestTest, FileDirCancelTest) { |
| // Put in mock resource provider. |
| NetModule::SetResourceProvider(TestNetResourceProvider); |
| |
| TestDelegate d; |
| { |
| base::FilePath file_path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &file_path); |
| file_path = file_path.Append(FILE_PATH_LITERAL("net")); |
| file_path = file_path.Append(FILE_PATH_LITERAL("data")); |
| |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| FilePathToFileURL(file_path), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| EXPECT_TRUE(req->is_pending()); |
| |
| d.set_cancel_in_received_data_pending(true); |
| |
| base::RunLoop().Run(); |
| } |
| |
| // Take out mock resource provider. |
| NetModule::SetResourceProvider(NULL); |
| } |
| |
| TEST_F(URLRequestTest, FileDirOutputSanity) { |
| // Verify the general sanity of the the output of the file: |
| // directory lister by checking for the output of a known existing |
| // file. |
| const char sentinel_name[] = "filedir-sentinel"; |
| |
| base::FilePath path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| FilePathToFileURL(path), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Generate entry for the sentinel file. |
| base::FilePath sentinel_path = path.AppendASCII(sentinel_name); |
| base::File::Info info; |
| EXPECT_TRUE(base::GetFileInfo(sentinel_path, &info)); |
| EXPECT_GT(info.size, 0); |
| std::string sentinel_output = GetDirectoryListingEntry( |
| base::string16(sentinel_name, sentinel_name + strlen(sentinel_name)), |
| std::string(sentinel_name), |
| false /* is_dir */, |
| info.size, |
| info.last_modified); |
| |
| ASSERT_LT(0, d.bytes_received()); |
| ASSERT_FALSE(d.request_failed()); |
| ASSERT_TRUE(req->status().is_success()); |
| // Check for the entry generated for the "sentinel" file. |
| const std::string& data = d.data_received(); |
| ASSERT_NE(data.find(sentinel_output), std::string::npos); |
| } |
| |
| TEST_F(URLRequestTest, FileDirRedirectNoCrash) { |
| // There is an implicit redirect when loading a file path that matches a |
| // directory and does not end with a slash. Ensure that following such |
| // redirects does not crash. See http://crbug.com/18686. |
| |
| base::FilePath path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| FilePathToFileURL(path), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| ASSERT_EQ(1, d.received_redirect_count()); |
| ASSERT_LT(0, d.bytes_received()); |
| ASSERT_FALSE(d.request_failed()); |
| ASSERT_TRUE(req->status().is_success()); |
| } |
| |
| #if defined(OS_WIN) |
| // Don't accept the url "file:///" on windows. See http://crbug.com/1474. |
| TEST_F(URLRequestTest, FileDirRedirectSingleSlash) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(GURL("file:///"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| ASSERT_EQ(1, d.received_redirect_count()); |
| ASSERT_FALSE(req->status().is_success()); |
| } |
| #endif // defined(OS_WIN) |
| |
| #endif // !defined(DISABLE_FILE_SUPPORT) |
| |
| TEST_F(URLRequestTest, InvalidUrlTest) { |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| GURL("invalid url"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| EXPECT_TRUE(d.request_failed()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, InvalidReferrerTest) { |
| TestURLRequestContext context; |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_cancel_request_with_policy_violating_referrer(true); |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://localhost/"), DEFAULT_PRIORITY, &d)); |
| req->SetReferrer("https://somewhere.com/"); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(d.request_failed()); |
| } |
| |
| #if defined(OS_WIN) |
| TEST_F(URLRequestTest, ResolveShortcutTest) { |
| base::FilePath app_path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &app_path); |
| app_path = app_path.Append(kTestFilePath); |
| app_path = app_path.AppendASCII("with-headers.html"); |
| |
| std::wstring lnk_path = app_path.value() + L".lnk"; |
| |
| base::win::ScopedCOMInitializer com_initializer; |
| |
| // Temporarily create a shortcut for test |
| { |
| base::win::ScopedComPtr<IShellLink> shell; |
| ASSERT_TRUE(SUCCEEDED(shell.CreateInstance(CLSID_ShellLink, NULL, |
| CLSCTX_INPROC_SERVER))); |
| base::win::ScopedComPtr<IPersistFile> persist; |
| ASSERT_TRUE(SUCCEEDED(shell.QueryInterface(persist.Receive()))); |
| EXPECT_TRUE(SUCCEEDED(shell->SetPath(app_path.value().c_str()))); |
| EXPECT_TRUE(SUCCEEDED(shell->SetDescription(L"ResolveShortcutTest"))); |
| EXPECT_TRUE(SUCCEEDED(persist->Save(lnk_path.c_str(), TRUE))); |
| } |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| FilePathToFileURL(base::FilePath(lnk_path)), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| WIN32_FILE_ATTRIBUTE_DATA data; |
| GetFileAttributesEx(app_path.value().c_str(), |
| GetFileExInfoStandard, &data); |
| HANDLE file = CreateFile(app_path.value().c_str(), GENERIC_READ, |
| FILE_SHARE_READ, NULL, OPEN_EXISTING, |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| EXPECT_NE(INVALID_HANDLE_VALUE, file); |
| scoped_ptr<char[]> buffer(new char[data.nFileSizeLow]); |
| DWORD read_size; |
| BOOL result; |
| result = ReadFile(file, buffer.get(), data.nFileSizeLow, |
| &read_size, NULL); |
| std::string content(buffer.get(), read_size); |
| CloseHandle(file); |
| |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(content, d.data_received()); |
| } |
| |
| // Clean the shortcut |
| DeleteFile(lnk_path.c_str()); |
| } |
| #endif // defined(OS_WIN) |
| |
| // Custom URLRequestJobs for use with interceptor tests |
| class RestartTestJob : public URLRequestTestJob { |
| public: |
| RestartTestJob(URLRequest* request, NetworkDelegate* network_delegate) |
| : URLRequestTestJob(request, network_delegate, true) {} |
| protected: |
| void StartAsync() override { this->NotifyRestartRequired(); } |
| private: |
| ~RestartTestJob() override {} |
| }; |
| |
| class CancelTestJob : public URLRequestTestJob { |
| public: |
| explicit CancelTestJob(URLRequest* request, NetworkDelegate* network_delegate) |
| : URLRequestTestJob(request, network_delegate, true) {} |
| protected: |
| void StartAsync() override { request_->Cancel(); } |
| private: |
| ~CancelTestJob() override {} |
| }; |
| |
| class CancelThenRestartTestJob : public URLRequestTestJob { |
| public: |
| explicit CancelThenRestartTestJob(URLRequest* request, |
| NetworkDelegate* network_delegate) |
| : URLRequestTestJob(request, network_delegate, true) { |
| } |
| protected: |
| void StartAsync() override { |
| request_->Cancel(); |
| this->NotifyRestartRequired(); |
| } |
| private: |
| ~CancelThenRestartTestJob() override {} |
| }; |
| |
| // An Interceptor for use with interceptor tests. |
| class MockURLRequestInterceptor : public URLRequestInterceptor { |
| public: |
| // Static getters for canned response header and data strings. |
| static std::string ok_data() { |
| return URLRequestTestJob::test_data_1(); |
| } |
| |
| static std::string ok_headers() { |
| return URLRequestTestJob::test_headers(); |
| } |
| |
| static std::string redirect_data() { |
| return std::string(); |
| } |
| |
| static std::string redirect_headers() { |
| return URLRequestTestJob::test_redirect_headers(); |
| } |
| |
| static std::string error_data() { |
| return std::string("ohhh nooooo mr. bill!"); |
| } |
| |
| static std::string error_headers() { |
| return URLRequestTestJob::test_error_headers(); |
| } |
| |
| MockURLRequestInterceptor() |
| : intercept_main_request_(false), restart_main_request_(false), |
| cancel_main_request_(false), cancel_then_restart_main_request_(false), |
| simulate_main_network_error_(false), |
| intercept_redirect_(false), cancel_redirect_request_(false), |
| intercept_final_response_(false), cancel_final_request_(false), |
| use_url_request_http_job_(false), |
| did_intercept_main_(false), did_restart_main_(false), |
| did_cancel_main_(false), did_cancel_then_restart_main_(false), |
| did_simulate_error_main_(false), |
| did_intercept_redirect_(false), did_cancel_redirect_(false), |
| did_intercept_final_(false), did_cancel_final_(false) { |
| } |
| |
| ~MockURLRequestInterceptor() override { |
| } |
| |
| // URLRequestInterceptor implementation: |
| URLRequestJob* MaybeInterceptRequest( |
| URLRequest* request, |
| NetworkDelegate* network_delegate) const override { |
| if (restart_main_request_) { |
| restart_main_request_ = false; |
| did_restart_main_ = true; |
| return new RestartTestJob(request, network_delegate); |
| } |
| if (cancel_main_request_) { |
| cancel_main_request_ = false; |
| did_cancel_main_ = true; |
| return new CancelTestJob(request, network_delegate); |
| } |
| if (cancel_then_restart_main_request_) { |
| cancel_then_restart_main_request_ = false; |
| did_cancel_then_restart_main_ = true; |
| return new CancelThenRestartTestJob(request, network_delegate); |
| } |
| if (simulate_main_network_error_) { |
| simulate_main_network_error_ = false; |
| did_simulate_error_main_ = true; |
| if (use_url_request_http_job_) { |
| return URLRequestHttpJob::Factory(request, network_delegate, "http"); |
| } |
| // This job will result in error since the requested URL is not one of the |
| // URLs supported by these tests. |
| return new URLRequestTestJob(request, network_delegate, true); |
| } |
| if (!intercept_main_request_) |
| return nullptr; |
| intercept_main_request_ = false; |
| did_intercept_main_ = true; |
| URLRequestTestJob* job = new URLRequestTestJob(request, |
| network_delegate, |
| main_headers_, |
| main_data_, |
| true); |
| job->set_load_timing_info(main_request_load_timing_info_); |
| return job; |
| } |
| |
| URLRequestJob* MaybeInterceptRedirect(URLRequest* request, |
| NetworkDelegate* network_delegate, |
| const GURL& location) const override { |
| if (cancel_redirect_request_) { |
| cancel_redirect_request_ = false; |
| did_cancel_redirect_ = true; |
| return new CancelTestJob(request, network_delegate); |
| } |
| if (!intercept_redirect_) |
| return nullptr; |
| intercept_redirect_ = false; |
| did_intercept_redirect_ = true; |
| if (use_url_request_http_job_) { |
| return URLRequestHttpJob::Factory(request, network_delegate, "http"); |
| } |
| return new URLRequestTestJob(request, |
| network_delegate, |
| redirect_headers_, |
| redirect_data_, |
| true); |
| } |
| |
| URLRequestJob* MaybeInterceptResponse( |
| URLRequest* request, |
| NetworkDelegate* network_delegate) const override { |
| if (cancel_final_request_) { |
| cancel_final_request_ = false; |
| did_cancel_final_ = true; |
| return new CancelTestJob(request, network_delegate); |
| } |
| if (!intercept_final_response_) |
| return nullptr; |
| intercept_final_response_ = false; |
| did_intercept_final_ = true; |
| if (use_url_request_http_job_) { |
| return URLRequestHttpJob::Factory(request, network_delegate, "http"); |
| } |
| return new URLRequestTestJob(request, |
| network_delegate, |
| final_headers_, |
| final_data_, |
| true); |
| } |
| |
| void set_intercept_main_request(bool intercept_main_request) { |
| intercept_main_request_ = intercept_main_request; |
| } |
| |
| void set_main_headers(const std::string& main_headers) { |
| main_headers_ = main_headers; |
| } |
| |
| void set_main_data(const std::string& main_data) { |
| main_data_ = main_data; |
| } |
| |
| void set_main_request_load_timing_info( |
| const LoadTimingInfo& main_request_load_timing_info) { |
| main_request_load_timing_info_ = main_request_load_timing_info; |
| } |
| |
| void set_restart_main_request(bool restart_main_request) { |
| restart_main_request_ = restart_main_request; |
| } |
| |
| void set_cancel_main_request(bool cancel_main_request) { |
| cancel_main_request_ = cancel_main_request; |
| } |
| |
| void set_cancel_then_restart_main_request( |
| bool cancel_then_restart_main_request) { |
| cancel_then_restart_main_request_ = cancel_then_restart_main_request; |
| } |
| |
| void set_simulate_main_network_error(bool simulate_main_network_error) { |
| simulate_main_network_error_ = simulate_main_network_error; |
| } |
| |
| void set_intercept_redirect(bool intercept_redirect) { |
| intercept_redirect_ = intercept_redirect; |
| } |
| |
| void set_redirect_headers(const std::string& redirect_headers) { |
| redirect_headers_ = redirect_headers; |
| } |
| |
| void set_redirect_data(const std::string& redirect_data) { |
| redirect_data_ = redirect_data; |
| } |
| |
| void set_cancel_redirect_request(bool cancel_redirect_request) { |
| cancel_redirect_request_ = cancel_redirect_request; |
| } |
| |
| void set_intercept_final_response(bool intercept_final_response) { |
| intercept_final_response_ = intercept_final_response; |
| } |
| |
| void set_final_headers(const std::string& final_headers) { |
| final_headers_ = final_headers; |
| } |
| |
| void set_final_data(const std::string& final_data) { |
| final_data_ = final_data; |
| } |
| |
| void set_cancel_final_request(bool cancel_final_request) { |
| cancel_final_request_ = cancel_final_request; |
| } |
| |
| void set_use_url_request_http_job(bool use_url_request_http_job) { |
| use_url_request_http_job_ = use_url_request_http_job; |
| } |
| |
| bool did_intercept_main() const { |
| return did_intercept_main_; |
| } |
| |
| bool did_restart_main() const { |
| return did_restart_main_; |
| } |
| |
| bool did_cancel_main() const { |
| return did_cancel_main_; |
| } |
| |
| bool did_cancel_then_restart_main() const { |
| return did_cancel_then_restart_main_; |
| } |
| |
| bool did_simulate_error_main() const { |
| return did_simulate_error_main_; |
| } |
| |
| bool did_intercept_redirect() const { |
| return did_intercept_redirect_; |
| } |
| |
| bool did_cancel_redirect() const { |
| return did_cancel_redirect_; |
| } |
| |
| bool did_intercept_final() const { |
| return did_intercept_final_; |
| } |
| |
| bool did_cancel_final() const { |
| return did_cancel_final_; |
| } |
| |
| private: |
| // Indicate whether to intercept the main request, and if so specify the |
| // response to return and the LoadTimingInfo to use. |
| mutable bool intercept_main_request_; |
| mutable std::string main_headers_; |
| mutable std::string main_data_; |
| mutable LoadTimingInfo main_request_load_timing_info_; |
| |
| // These indicate actions that can be taken within MaybeInterceptRequest. |
| mutable bool restart_main_request_; |
| mutable bool cancel_main_request_; |
| mutable bool cancel_then_restart_main_request_; |
| mutable bool simulate_main_network_error_; |
| |
| // Indicate whether to intercept redirects, and if so specify the response to |
| // return. |
| mutable bool intercept_redirect_; |
| mutable std::string redirect_headers_; |
| mutable std::string redirect_data_; |
| |
| // Cancel the request within MaybeInterceptRedirect. |
| mutable bool cancel_redirect_request_; |
| |
| // Indicate whether to intercept the final response, and if so specify the |
| // response to return. |
| mutable bool intercept_final_response_; |
| mutable std::string final_headers_; |
| mutable std::string final_data_; |
| |
| // Cancel the final request within MaybeInterceptResponse. |
| mutable bool cancel_final_request_; |
| |
| // Instruct the interceptor to use a real URLRequestHTTPJob. |
| mutable bool use_url_request_http_job_; |
| |
| // These indicate if the interceptor did something or not. |
| mutable bool did_intercept_main_; |
| mutable bool did_restart_main_; |
| mutable bool did_cancel_main_; |
| mutable bool did_cancel_then_restart_main_; |
| mutable bool did_simulate_error_main_; |
| mutable bool did_intercept_redirect_; |
| mutable bool did_cancel_redirect_; |
| mutable bool did_intercept_final_; |
| mutable bool did_cancel_final_; |
| }; |
| |
| // Inherit PlatformTest since we require the autorelease pool on Mac OS X. |
| class URLRequestInterceptorTest : public URLRequestTest { |
| public: |
| URLRequestInterceptorTest() : URLRequestTest(), interceptor_(NULL) { |
| } |
| |
| ~URLRequestInterceptorTest() override { |
| // URLRequestJobs may post clean-up tasks on destruction. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetUpFactory() override { |
| interceptor_ = new MockURLRequestInterceptor(); |
| job_factory_.reset(new URLRequestInterceptingJobFactory( |
| std::move(job_factory_), make_scoped_ptr(interceptor_))); |
| } |
| |
| MockURLRequestInterceptor* interceptor() const { |
| return interceptor_; |
| } |
| |
| private: |
| MockURLRequestInterceptor* interceptor_; |
| }; |
| |
| TEST_F(URLRequestInterceptorTest, Intercept) { |
| // Intercept the main request and respond with a simple response. |
| interceptor()->set_intercept_main_request(true); |
| interceptor()->set_main_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_main_data(MockURLRequestInterceptor::ok_data()); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| base::SupportsUserData::Data* user_data0 = new base::SupportsUserData::Data(); |
| base::SupportsUserData::Data* user_data1 = new base::SupportsUserData::Data(); |
| base::SupportsUserData::Data* user_data2 = new base::SupportsUserData::Data(); |
| req->SetUserData(nullptr, user_data0); |
| req->SetUserData(&user_data1, user_data1); |
| req->SetUserData(&user_data2, user_data2); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Make sure we can retrieve our specific user data. |
| EXPECT_EQ(user_data0, req->GetUserData(nullptr)); |
| EXPECT_EQ(user_data1, req->GetUserData(&user_data1)); |
| EXPECT_EQ(user_data2, req->GetUserData(&user_data2)); |
| |
| // Check that we got one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptRedirect) { |
| // Intercept the main request and respond with a redirect. |
| interceptor()->set_intercept_main_request(true); |
| interceptor()->set_main_headers( |
| MockURLRequestInterceptor::redirect_headers()); |
| interceptor()->set_main_data(MockURLRequestInterceptor::redirect_data()); |
| |
| // Intercept that redirect and respond with a final OK response. |
| interceptor()->set_intercept_redirect(true); |
| interceptor()->set_redirect_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_redirect_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_intercept_main()); |
| EXPECT_TRUE(interceptor()->did_intercept_redirect()); |
| |
| // Check that we got one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| if (req->status().is_success()) |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptServerError) { |
| // Intercept the main request to generate a server error response. |
| interceptor()->set_intercept_main_request(true); |
| interceptor()->set_main_headers(MockURLRequestInterceptor::error_headers()); |
| interceptor()->set_main_data(MockURLRequestInterceptor::error_data()); |
| |
| // Intercept that error and respond with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_intercept_main()); |
| EXPECT_TRUE(interceptor()->did_intercept_final()); |
| |
| // Check that we got one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptNetworkError) { |
| // Intercept the main request to simulate a network error. |
| interceptor()->set_simulate_main_network_error(true); |
| |
| // Intercept that error and respond with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_simulate_error_main()); |
| EXPECT_TRUE(interceptor()->did_intercept_final()); |
| |
| // Check that we received one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptRestartRequired) { |
| // Restart the main request. |
| interceptor()->set_restart_main_request(true); |
| |
| // then intercept the new main request and respond with an OK response |
| interceptor()->set_intercept_main_request(true); |
| interceptor()->set_main_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_main_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_restart_main()); |
| EXPECT_TRUE(interceptor()->did_intercept_main()); |
| |
| // Check that we received one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| if (req->status().is_success()) |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelMain) { |
| // Intercept the main request and cancel from within the restarted job. |
| interceptor()->set_cancel_main_request(true); |
| |
| // Set up to intercept the final response and override it with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_cancel_main()); |
| EXPECT_FALSE(interceptor()->did_intercept_final()); |
| |
| // Check that we see a canceled request. |
| EXPECT_FALSE(req->status().is_success()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelRedirect) { |
| // Intercept the main request and respond with a redirect. |
| interceptor()->set_intercept_main_request(true); |
| interceptor()->set_main_headers( |
| MockURLRequestInterceptor::redirect_headers()); |
| interceptor()->set_main_data(MockURLRequestInterceptor::redirect_data()); |
| |
| // Intercept the redirect and cancel from within that job. |
| interceptor()->set_cancel_redirect_request(true); |
| |
| // Set up to intercept the final response and override it with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_intercept_main()); |
| EXPECT_TRUE(interceptor()->did_cancel_redirect()); |
| EXPECT_FALSE(interceptor()->did_intercept_final()); |
| |
| // Check that we see a canceled request. |
| EXPECT_FALSE(req->status().is_success()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelFinal) { |
| // Intercept the main request to simulate a network error. |
| interceptor()->set_simulate_main_network_error(true); |
| |
| // Set up to intercept final the response and cancel from within that job. |
| interceptor()->set_cancel_final_request(true); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_simulate_error_main()); |
| EXPECT_TRUE(interceptor()->did_cancel_final()); |
| |
| // Check that we see a canceled request. |
| EXPECT_FALSE(req->status().is_success()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| |
| TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelInRestart) { |
| // Intercept the main request and cancel then restart from within that job. |
| interceptor()->set_cancel_then_restart_main_request(true); |
| |
| // Set up to intercept the final response and override it with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that the interceptor got called as expected. |
| EXPECT_TRUE(interceptor()->did_cancel_then_restart_main()); |
| EXPECT_FALSE(interceptor()->did_intercept_final()); |
| |
| // Check that we see a canceled request. |
| EXPECT_FALSE(req->status().is_success()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| |
| // "Normal" LoadTimingInfo as returned by a job. Everything is in order, not |
| // reused. |connect_time_flags| is used to indicate if there should be dns |
| // or SSL times, and |used_proxy| is used for proxy times. |
| LoadTimingInfo NormalLoadTimingInfo(base::TimeTicks now, |
| int connect_time_flags, |
| bool used_proxy) { |
| LoadTimingInfo load_timing; |
| load_timing.socket_log_id = 1; |
| |
| if (used_proxy) { |
| load_timing.proxy_resolve_start = now + base::TimeDelta::FromDays(1); |
| load_timing.proxy_resolve_end = now + base::TimeDelta::FromDays(2); |
| } |
| |
| LoadTimingInfo::ConnectTiming& connect_timing = load_timing.connect_timing; |
| if (connect_time_flags & CONNECT_TIMING_HAS_DNS_TIMES) { |
| connect_timing.dns_start = now + base::TimeDelta::FromDays(3); |
| connect_timing.dns_end = now + base::TimeDelta::FromDays(4); |
| } |
| connect_timing.connect_start = now + base::TimeDelta::FromDays(5); |
| if (connect_time_flags & CONNECT_TIMING_HAS_SSL_TIMES) { |
| connect_timing.ssl_start = now + base::TimeDelta::FromDays(6); |
| connect_timing.ssl_end = now + base::TimeDelta::FromDays(7); |
| } |
| connect_timing.connect_end = now + base::TimeDelta::FromDays(8); |
| |
| load_timing.send_start = now + base::TimeDelta::FromDays(9); |
| load_timing.send_end = now + base::TimeDelta::FromDays(10); |
| load_timing.receive_headers_end = now + base::TimeDelta::FromDays(11); |
| return load_timing; |
| } |
| |
| // Same as above, but in the case of a reused socket. |
| LoadTimingInfo NormalLoadTimingInfoReused(base::TimeTicks now, |
| bool used_proxy) { |
| LoadTimingInfo load_timing; |
| load_timing.socket_log_id = 1; |
| load_timing.socket_reused = true; |
| |
| if (used_proxy) { |
| load_timing.proxy_resolve_start = now + base::TimeDelta::FromDays(1); |
| load_timing.proxy_resolve_end = now + base::TimeDelta::FromDays(2); |
| } |
| |
| load_timing.send_start = now + base::TimeDelta::FromDays(9); |
| load_timing.send_end = now + base::TimeDelta::FromDays(10); |
| load_timing.receive_headers_end = now + base::TimeDelta::FromDays(11); |
| return load_timing; |
| } |
| |
| LoadTimingInfo RunURLRequestInterceptorLoadTimingTest( |
| const LoadTimingInfo& job_load_timing, |
| const URLRequestContext& context, |
| MockURLRequestInterceptor* interceptor) { |
| interceptor->set_intercept_main_request(true); |
| interceptor->set_main_request_load_timing_info(job_load_timing); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| LoadTimingInfo resulting_load_timing; |
| req->GetLoadTimingInfo(&resulting_load_timing); |
| |
| // None of these should be modified by the URLRequest. |
| EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused); |
| EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id); |
| EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start); |
| EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end); |
| EXPECT_EQ(job_load_timing.receive_headers_end, |
| resulting_load_timing.receive_headers_end); |
| |
| return resulting_load_timing; |
| } |
| |
| // Basic test that the intercept + load timing tests work. |
| TEST_F(URLRequestInterceptorTest, InterceptLoadTiming) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, false); |
| |
| LoadTimingInfo load_timing_result = |
| RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Nothing should have been changed by the URLRequest. |
| EXPECT_EQ(job_load_timing.proxy_resolve_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(job_load_timing.proxy_resolve_end, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_start, |
| load_timing_result.connect_timing.dns_start); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_end, |
| load_timing_result.connect_timing.dns_end); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_end, |
| load_timing_result.connect_timing.connect_end); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_end, |
| load_timing_result.connect_timing.ssl_end); |
| |
| // Redundant sanity check. |
| TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Another basic test, with proxy and SSL times, but no DNS times. |
| TEST_F(URLRequestInterceptorTest, InterceptLoadTimingProxy) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true); |
| |
| LoadTimingInfo load_timing_result = |
| RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Nothing should have been changed by the URLRequest. |
| EXPECT_EQ(job_load_timing.proxy_resolve_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(job_load_timing.proxy_resolve_end, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_start, |
| load_timing_result.connect_timing.dns_start); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_end, |
| load_timing_result.connect_timing.dns_end); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_end, |
| load_timing_result.connect_timing.connect_end); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_end, |
| load_timing_result.connect_timing.ssl_end); |
| |
| // Redundant sanity check. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_SSL_TIMES); |
| } |
| |
| // Make sure that URLRequest correctly adjusts proxy times when they're before |
| // |request_start|, due to already having a connected socket. This happens in |
| // the case of reusing a SPDY session. The connected socket is not considered |
| // reused in this test (May be a preconnect). |
| // |
| // To mix things up from the test above, assumes DNS times but no SSL times. |
| TEST_F(URLRequestInterceptorTest, InterceptLoadTimingEarlyProxyResolution) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true); |
| job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(6); |
| job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(5); |
| job_load_timing.connect_timing.dns_start = now - base::TimeDelta::FromDays(4); |
| job_load_timing.connect_timing.dns_end = now - base::TimeDelta::FromDays(3); |
| job_load_timing.connect_timing.connect_start = |
| now - base::TimeDelta::FromDays(2); |
| job_load_timing.connect_timing.connect_end = |
| now - base::TimeDelta::FromDays(1); |
| |
| LoadTimingInfo load_timing_result = |
| RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Proxy times, connect times, and DNS times should all be replaced with |
| // request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.dns_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.dns_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Same as above, but in the reused case. |
| TEST_F(URLRequestInterceptorTest, |
| InterceptLoadTimingEarlyProxyResolutionReused) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true); |
| job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(4); |
| job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(3); |
| |
| LoadTimingInfo load_timing_result = |
| RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Proxy times and connect times should all be replaced with request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingReusedWithProxy(load_timing_result); |
| } |
| |
| // Make sure that URLRequest correctly adjusts connect times when they're before |
| // |request_start|, due to reusing a connected socket. The connected socket is |
| // not considered reused in this test (May be a preconnect). |
| // |
| // To mix things up, the request has SSL times, but no DNS times. |
| TEST_F(URLRequestInterceptorTest, InterceptLoadTimingEarlyConnect) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, false); |
| job_load_timing.connect_timing.connect_start = |
| now - base::TimeDelta::FromDays(1); |
| job_load_timing.connect_timing.ssl_start = now - base::TimeDelta::FromDays(2); |
| job_load_timing.connect_timing.ssl_end = now - base::TimeDelta::FromDays(3); |
| job_load_timing.connect_timing.connect_end = |
| now - base::TimeDelta::FromDays(4); |
| |
| LoadTimingInfo load_timing_result = |
| RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Connect times, and SSL times should be replaced with request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.ssl_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_SSL_TIMES); |
| } |
| |
| // Make sure that URLRequest correctly adjusts connect times when they're before |
| // |request_start|, due to reusing a connected socket in the case that there |
| // are also proxy times. The connected socket is not considered reused in this |
| // test (May be a preconnect). |
| // |
| // In this test, there are no SSL or DNS times. |
| TEST_F(URLRequestInterceptorTest, InterceptLoadTimingEarlyConnectWithProxy) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY, true); |
| job_load_timing.connect_timing.connect_start = |
| now - base::TimeDelta::FromDays(1); |
| job_load_timing.connect_timing.connect_end = |
| now - base::TimeDelta::FromDays(2); |
| |
| LoadTimingInfo load_timing_result = |
| RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Connect times should be replaced with proxy_resolve_end. |
| EXPECT_EQ(load_timing_result.proxy_resolve_end, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.proxy_resolve_end, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); |
| } |
| |
| // Check that two different URL requests have different identifiers. |
| TEST_F(URLRequestTest, Identifiers) { |
| TestDelegate d; |
| TestURLRequestContext context; |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d)); |
| scoped_ptr<URLRequest> other_req( |
| context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d)); |
| |
| ASSERT_NE(req->identifier(), other_req->identifier()); |
| } |
| |
| #if defined(OS_IOS) |
| // TODO(droger): Check that a failure to connect to the proxy is reported to |
| // the network delegate. crbug.com/496743 |
| #define MAYBE_NetworkDelegateProxyError DISABLED_NetworkDelegateProxyError |
| #else |
| #define MAYBE_NetworkDelegateProxyError NetworkDelegateProxyError |
| #endif |
| TEST_F(URLRequestTest, MAYBE_NetworkDelegateProxyError) { |
| MockHostResolver host_resolver; |
| host_resolver.rules()->AddSimulatedFailure("*"); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequests. |
| TestURLRequestContextWithProxy context("myproxy:70", &network_delegate); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| // Check we see a failed request. |
| EXPECT_FALSE(req->status().is_success()); |
| // The proxy server is not set before failure. |
| EXPECT_TRUE(req->proxy_server().IsEmpty()); |
| EXPECT_EQ(URLRequestStatus::FAILED, req->status().status()); |
| EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, req->status().error()); |
| |
| EXPECT_EQ(1, network_delegate.error_count()); |
| EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, network_delegate.last_error()); |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| } |
| |
| // Make sure that NetworkDelegate::NotifyCompleted is called if |
| // content is empty. |
| TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(GURL("data:,"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ("", d.data_received()); |
| EXPECT_EQ(1, default_network_delegate_.completed_requests()); |
| } |
| |
| // Make sure that SetPriority actually sets the URLRequest's priority |
| // correctly, both before and after start. |
| TEST_F(URLRequestTest, SetPriorityBasic) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); |
| |
| req->SetPriority(LOW); |
| EXPECT_EQ(LOW, req->priority()); |
| |
| req->Start(); |
| EXPECT_EQ(LOW, req->priority()); |
| |
| req->SetPriority(MEDIUM); |
| EXPECT_EQ(MEDIUM, req->priority()); |
| } |
| |
| // Make sure that URLRequest calls SetPriority on a job before calling |
| // Start on it. |
| TEST_F(URLRequestTest, SetJobPriorityBeforeJobStart) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); |
| |
| RequestPriority job_priority; |
| scoped_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| EXPECT_EQ(DEFAULT_PRIORITY, job_priority); |
| |
| req->SetPriority(LOW); |
| |
| req->Start(); |
| EXPECT_EQ(LOW, job_priority); |
| } |
| |
| // Make sure that URLRequest passes on its priority updates to its |
| // job. |
| TEST_F(URLRequestTest, SetJobPriority) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| |
| RequestPriority job_priority; |
| scoped_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| req->SetPriority(LOW); |
| req->Start(); |
| EXPECT_EQ(LOW, job_priority); |
| |
| req->SetPriority(MEDIUM); |
| EXPECT_EQ(MEDIUM, req->priority()); |
| EXPECT_EQ(MEDIUM, job_priority); |
| } |
| |
| // Setting the IGNORE_LIMITS load flag should be okay if the priority |
| // is MAXIMUM_PRIORITY. |
| TEST_F(URLRequestTest, PriorityIgnoreLimits) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| GURL("http://test_intercept/foo"), MAXIMUM_PRIORITY, &d)); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| |
| RequestPriority job_priority; |
| scoped_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| req->SetLoadFlags(LOAD_IGNORE_LIMITS); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| |
| req->SetPriority(MAXIMUM_PRIORITY); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| |
| req->Start(); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| EXPECT_EQ(MAXIMUM_PRIORITY, job_priority); |
| } |
| |
| namespace { |
| |
| // Less verbose way of running a simple testserver for the tests below. |
| class LocalHttpTestServer : public EmbeddedTestServer { |
| public: |
| explicit LocalHttpTestServer(const base::FilePath& document_root) { |
| AddDefaultHandlers(document_root); |
| } |
| |
| LocalHttpTestServer() { AddDefaultHandlers(base::FilePath()); } |
| }; |
| |
| } // namespace |
| |
| TEST_F(URLRequestTest, DelayedCookieCallback) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestURLRequestContext context; |
| scoped_ptr<DelayedCookieMonster> delayed_cm(new DelayedCookieMonster()); |
| context.set_cookie_store(delayed_cm.get()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(1, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") |
| != std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") |
| != std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent when LOAD_DO_NOT_SEND_COOKIES is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") |
| == std::string::npos); |
| |
| // LOAD_DO_NOT_SEND_COOKIES does not trigger OnGetCookies. |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveCookies) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(1, network_delegate.set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| DEFAULT_PRIORITY, &d)); |
| req->SetLoadFlags(LOAD_DO_NOT_SAVE_COOKIES); |
| req->Start(); |
| |
| base::RunLoop().Run(); |
| |
| // LOAD_DO_NOT_SAVE_COOKIES does not trigger OnSetCookie. |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") |
| == std::string::npos); |
| EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") |
| != std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") |
| != std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_GET_COOKIES); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") |
| == std::string::npos); |
| |
| EXPECT_EQ(1, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // TODO(crbug.com/564656) This test is flaky on iOS. |
| #if defined(OS_IOS) |
| #define MAYBE_DoNotSaveCookies_ViaPolicy FLAKY_DoNotSaveCookies_ViaPolicy |
| #else |
| #define MAYBE_DoNotSaveCookies_ViaPolicy DoNotSaveCookies_ViaPolicy |
| #endif |
| TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_SET_COOKIE); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(2, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") |
| == std::string::npos); |
| EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") |
| != std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveEmptyCookies) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up an empty cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy_Async) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") |
| != std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_GET_COOKIES); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") |
| == std::string::npos); |
| |
| EXPECT_EQ(1, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy_Async) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_SET_COOKIE); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(2, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") |
| == std::string::npos); |
| EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") |
| != std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SameSiteCookiesEnabled) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_experimental_cookie_features_enabled(true); |
| default_context_.set_network_delegate(&network_delegate); |
| |
| const std::string kHost = "example.test"; |
| const std::string kSubHost = "subdomain.example.test"; |
| const std::string kCrossHost = "cross-origin.test"; |
| |
| // Set up two 'SameSite' cookies on 'example.test' |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(2, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that both cookies are sent for same-site requests. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(test_server.GetURL(kHost, "/")); |
| req->set_initiator(url::Origin(test_server.GetURL(kHost, "/"))); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that both cookies are sent for same-registrable-domain requests. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(test_server.GetURL(kSubHost, "/")); |
| req->set_initiator(url::Origin(test_server.GetURL(kSubHost, "/"))); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that neither cookie is not sent for cross-site requests. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(test_server.GetURL(kCrossHost, "/")); |
| req->set_initiator(url::Origin(test_server.GetURL(kCrossHost, "/"))); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the lax cookie is sent for cross-site initiators when the |
| // method is "safe". |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(test_server.GetURL(kHost, "/")); |
| req->set_initiator(url::Origin(test_server.GetURL(kCrossHost, "/"))); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that neither cookie is sent for cross-site initiators when the |
| // method is unsafe (e.g. POST). |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(test_server.GetURL(kHost, "/")); |
| req->set_initiator(url::Origin(test_server.GetURL(kCrossHost, "/"))); |
| req->set_method("POST"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SameSiteCookiesDisabled) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a 'SameSite' cookie (on '127.0.0.1', as that's where |
| // LocalHttpTestServer points). |
| { |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_experimental_cookie_features_enabled(false); |
| default_context_.set_network_delegate(&network_delegate); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(2, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that the cookie is sent for same-site requests. |
| { |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_experimental_cookie_features_enabled(false); |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(test_server.GetURL("/")); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("StrictSameSiteCookie=1") != |
| std::string::npos); |
| EXPECT_TRUE(d.data_received().find("LaxSameSiteCookie=1") != |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is also sent for cross-site requests. |
| { |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_experimental_cookie_features_enabled(false); |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->set_first_party_for_cookies(GURL("http://cross-site.test/")); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_NE(d.data_received().find("StrictSameSiteCookie=1"), |
| std::string::npos); |
| EXPECT_TRUE(d.data_received().find("LaxSameSiteCookie=1") != |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests that __Secure- cookies can't be set on non-secure origins. |
| TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) { |
| EmbeddedTestServer http_server; |
| http_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| http_server.GetURL("/set-cookie?__Secure-nonsecure-origin=1;Secure"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(d.data_received().find("__Secure-nonsecure-origin=1"), |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SecureCookiePrefixNonsecure) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a non-Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(https_server.GetURL("/set-cookie?__Secure-foo=1"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(d.data_received().find("__Secure-foo=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SecureCookiePrefixSecure) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/set-cookie?__Secure-bar=1;Secure"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_NE(d.data_received().find("__Secure-bar=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests that secure cookies can't be set on non-secure origins if strict secure |
| // cookies are enabled. |
| TEST_F(URLRequestTest, StrictSecureCookiesOnNonsecureOrigin) { |
| EmbeddedTestServer http_server; |
| http_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestExperimentalFeaturesNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure cookie, with experimental features enabled. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| http_server.GetURL("/set-cookie?nonsecure-origin=1;Secure"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(d.data_received().find("nonsecure-origin=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests that secure cookies can be set on secure origins even if strict secure |
| // cookies are enabled. |
| TEST_F(URLRequestTest, StrictSecureCookiesOnSecureOrigin) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestExperimentalFeaturesNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure cookie, with experimental features enabled. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/set-cookie?secure-origin=1;Secure"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_NE(d.data_received().find("secure-origin=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests that a request is cancelled while entering suspend mode. Uses mocks |
| // rather than a spawned test server because the connection used to talk to |
| // the test server is affected by entering suspend mode on Android. |
| TEST_F(URLRequestTest, CancelOnSuspend) { |
| TestPowerMonitorSource* power_monitor_source = new TestPowerMonitorSource(); |
| base::PowerMonitor power_monitor(make_scoped_ptr(power_monitor_source)); |
| |
| URLRequestFailedJob::AddUrlHandler(); |
| |
| TestDelegate d; |
| // Request that just hangs. |
| GURL url(URLRequestFailedJob::GetMockHttpUrl(ERR_IO_PENDING)); |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| power_monitor_source->Suspend(); |
| // Wait for the suspend notification to cause the request to fail. |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_EQ(1, default_network_delegate_.completed_requests()); |
| |
| URLRequestFilter::GetInstance()->ClearHandlers(); |
| |
| // Shouldn't be needed, but just in case. |
| power_monitor_source->Resume(); |
| } |
| |
| // FixedDateNetworkDelegate swaps out the server's HTTP Date response header |
| // value for the |fixed_date| argument given to the constructor. |
| class FixedDateNetworkDelegate : public TestNetworkDelegate { |
| public: |
| explicit FixedDateNetworkDelegate(const std::string& fixed_date) |
| : fixed_date_(fixed_date) {} |
| ~FixedDateNetworkDelegate() override {} |
| |
| // NetworkDelegate implementation |
| int OnHeadersReceived( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| GURL* allowed_unsafe_redirect_url) override; |
| |
| private: |
| std::string fixed_date_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FixedDateNetworkDelegate); |
| }; |
| |
| int FixedDateNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| GURL* allowed_unsafe_redirect_url) { |
| HttpResponseHeaders* new_response_headers = |
| new HttpResponseHeaders(original_response_headers->raw_headers()); |
| |
| new_response_headers->RemoveHeader("Date"); |
| new_response_headers->AddHeader("Date: " + fixed_date_); |
| |
| *override_response_headers = new_response_headers; |
| return TestNetworkDelegate::OnHeadersReceived(request, |
| callback, |
| original_response_headers, |
| override_response_headers, |
| allowed_unsafe_redirect_url); |
| } |
| |
| // Test that cookie expiration times are adjusted for server/client clock |
| // skew and that we handle incorrect timezone specifier "UTC" in HTTP Date |
| // headers by defaulting to GMT. (crbug.com/135131) |
| TEST_F(URLRequestTest, AcceptClockSkewCookieWithWrongDateTimezone) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up an expired cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL( |
| "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| } |
| // Verify that the cookie is not set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("StillGood=1") == std::string::npos); |
| } |
| // Set up a cookie with clock skew and "UTC" HTTP Date timezone specifier. |
| { |
| FixedDateNetworkDelegate network_delegate("18-Apr-1977 22:49:13 UTC"); |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL( |
| "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| } |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context_.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("StillGood=1") != std::string::npos); |
| } |
| } |
| |
| |
| // Check that it is impossible to change the referrer in the extra headers of |
| // an URLRequest. |
| TEST_F(URLRequestTest, DoNotOverrideReferrer) { |
| LocalHttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // If extra headers contain referer and the request contains a referer, |
| // only the latter shall be respected. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); |
| req->SetReferrer("http://foo.com/"); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/"); |
| req->SetExtraRequestHeaders(headers); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ("http://foo.com/", d.data_received()); |
| } |
| |
| // If extra headers contain a referer but the request does not, no referer |
| // shall be sent in the header. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/"); |
| req->SetExtraRequestHeaders(headers); |
| req->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ("None", d.data_received()); |
| } |
| } |
| |
| class URLRequestTestHTTP : public URLRequestTest { |
| public: |
| URLRequestTestHTTP() : test_server_(base::FilePath(kTestFilePath)) {} |
| |
| protected: |
| // Requests |redirect_url|, which must return a HTTP 3xx redirect. |
| // |request_method| is the method to use for the initial request. |
| // |redirect_method| is the method that is expected to be used for the second |
| // request, after redirection. |
| // If |include_data| is true, data is uploaded with the request. The |
| // response body is expected to match it exactly, if and only if |
| // |request_method| == |redirect_method|. |
| void HTTPRedirectMethodTest(const GURL& redirect_url, |
| const std::string& request_method, |
| const std::string& redirect_method, |
| bool include_data) { |
| static const char kData[] = "hello world"; |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(redirect_url, DEFAULT_PRIORITY, &d)); |
| req->set_method(request_method); |
| if (include_data) { |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::SizeTToString(arraysize(kData) - 1)); |
| headers.SetHeader(HttpRequestHeaders::kContentType, "text/plain"); |
| req->SetExtraRequestHeaders(headers); |
| } |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(redirect_method, req->method()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_EQ(OK, req->status().error()); |
| if (include_data) { |
| if (request_method == redirect_method) { |
| EXPECT_TRUE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentLength)); |
| EXPECT_TRUE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentType)); |
| EXPECT_EQ(kData, d.data_received()); |
| } else { |
| EXPECT_FALSE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentLength)); |
| EXPECT_FALSE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentType)); |
| EXPECT_NE(kData, d.data_received()); |
| } |
| } |
| if (HasFailure()) |
| LOG(WARNING) << "Request method was: " << request_method; |
| } |
| |
| // Requests |redirect_url|, which must return a HTTP 3xx redirect. |
| // |request_method| is the method to use for the initial request. |
| // |redirect_method| is the method that is expected to be used for the second |
| // request, after redirection. |
| // |origin_value| is the expected value for the Origin header after |
| // redirection. If empty, expects that there will be no Origin header. |
| void HTTPRedirectOriginHeaderTest(const GURL& redirect_url, |
| const std::string& request_method, |
| const std::string& redirect_method, |
| const std::string& origin_value) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(redirect_url, DEFAULT_PRIORITY, &d)); |
| req->set_method(request_method); |
| req->SetExtraRequestHeaderByName(HttpRequestHeaders::kOrigin, |
| redirect_url.GetOrigin().spec(), false); |
| req->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(redirect_method, req->method()); |
| // Note that there is no check for request success here because, for |
| // purposes of testing, the request very well may fail. For example, if the |
| // test redirects to an HTTPS server from an HTTP origin, thus it is cross |
| // origin, there is not an HTTPS server in this unit test framework, so the |
| // request would fail. However, that's fine, as long as the request headers |
| // are in order and pass the checks below. |
| if (origin_value.empty()) { |
| EXPECT_FALSE( |
| req->extra_request_headers().HasHeader(HttpRequestHeaders::kOrigin)); |
| } else { |
| std::string origin_header; |
| EXPECT_TRUE(req->extra_request_headers().GetHeader( |
| HttpRequestHeaders::kOrigin, &origin_header)); |
| EXPECT_EQ(origin_value, origin_header); |
| } |
| } |
| |
| void HTTPUploadDataOperationTest(const std::string& method) { |
| const int kMsgSize = 20000; // multiple of 10 |
| const int kIterations = 50; |
| char* uploadBytes = new char[kMsgSize+1]; |
| char* ptr = uploadBytes; |
| char marker = 'a'; |
| for (int idx = 0; idx < kMsgSize/10; idx++) { |
| memcpy(ptr, "----------", 10); |
| ptr += 10; |
| if (idx % 100 == 0) { |
| ptr--; |
| *ptr++ = marker; |
| if (++marker > 'z') |
| marker = 'a'; |
| } |
| } |
| uploadBytes[kMsgSize] = '\0'; |
| |
| for (int i = 0; i < kIterations; ++i) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server_.GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->set_method(method.c_str()); |
| |
| r->set_upload(CreateSimpleUploadData(uploadBytes)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| ASSERT_EQ(1, d.response_started_count()) |
| << "request failed: " << r->status().status() |
| << ", os error: " << r->status().error(); |
| |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(uploadBytes, d.data_received()); |
| } |
| delete[] uploadBytes; |
| } |
| |
| void AddChunksToUpload(URLRequest* r) { |
| r->AppendChunkToUpload("a", 1, false); |
| r->AppendChunkToUpload("bcd", 3, false); |
| r->AppendChunkToUpload("this is a longer chunk than before.", 35, false); |
| r->AppendChunkToUpload("\r\n\r\n", 4, false); |
| r->AppendChunkToUpload("0", 1, false); |
| r->AppendChunkToUpload("2323", 4, true); |
| } |
| |
| void VerifyReceivedDataMatchesChunks(URLRequest* r, TestDelegate* d) { |
| // This should match the chunks sent by AddChunksToUpload(). |
| const std::string expected_data = |
| "abcdthis is a longer chunk than before.\r\n\r\n02323"; |
| |
| ASSERT_EQ(1, d->response_started_count()) |
| << "request failed: " << r->status().status() |
| << ", os error: " << r->status().error(); |
| |
| EXPECT_FALSE(d->received_data_before_response()); |
| |
| EXPECT_EQ(expected_data.size(), static_cast<size_t>(d->bytes_received())); |
| EXPECT_EQ(expected_data, d->data_received()); |
| } |
| |
| bool DoManyCookiesRequest(int num_cookies) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server_.GetURL("/set-many-cookies?" + |
| base::IntToString(num_cookies)), |
| DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| bool is_success = r->status().is_success(); |
| |
| if (!is_success) |
| EXPECT_TRUE(r->status().error() == ERR_RESPONSE_HEADERS_TOO_BIG); |
| |
| return is_success; |
| } |
| |
| LocalHttpTestServer* http_test_server() { return &test_server_; } |
| |
| private: |
| LocalHttpTestServer test_server_; |
| }; |
| |
| namespace { |
| |
| scoped_ptr<test_server::HttpResponse> HandleRedirectConnect( |
| const test_server::HttpRequest& request) { |
| if (request.headers.find("Host") == request.headers.end() || |
| request.headers.at("Host") != "www.redirect.com" || |
| request.method != test_server::METHOD_CONNECT) { |
| return nullptr; |
| } |
| |
| scoped_ptr<test_server::BasicHttpResponse> http_response( |
| new test_server::BasicHttpResponse); |
| http_response->set_code(HTTP_FOUND); |
| http_response->AddCustomHeader("Location", |
| "http://www.destination.com/foo.js"); |
| return std::move(http_response); |
| } |
| |
| } // namespace |
| |
| class TestSSLConfigService : public SSLConfigService { |
| public: |
| TestSSLConfigService(bool ev_enabled, |
| bool online_rev_checking, |
| bool rev_checking_required_local_anchors, |
| bool token_binding_enabled) |
| : ev_enabled_(ev_enabled), |
| online_rev_checking_(online_rev_checking), |
| rev_checking_required_local_anchors_( |
| rev_checking_required_local_anchors), |
| token_binding_enabled_(token_binding_enabled), |
| min_version_(kDefaultSSLVersionMin), |
| fallback_min_version_(kDefaultSSLVersionFallbackMin) {} |
| |
| void set_min_version(uint16_t version) { min_version_ = version; } |
| |
| void set_fallback_min_version(uint16_t version) { |
| fallback_min_version_ = version; |
| } |
| |
| // SSLConfigService: |
| void GetSSLConfig(SSLConfig* config) override { |
| *config = SSLConfig(); |
| config->rev_checking_enabled = online_rev_checking_; |
| config->verify_ev_cert = ev_enabled_; |
| config->rev_checking_required_local_anchors = |
| rev_checking_required_local_anchors_; |
| if (fallback_min_version_) { |
| config->version_fallback_min = fallback_min_version_; |
| } |
| if (min_version_) { |
| config->version_min = min_version_; |
| } |
| if (token_binding_enabled_) { |
| config->token_binding_params.push_back(TB_PARAM_ECDSAP256); |
| } |
| } |
| |
| protected: |
| ~TestSSLConfigService() override {} |
| |
| private: |
| const bool ev_enabled_; |
| const bool online_rev_checking_; |
| const bool rev_checking_required_local_anchors_; |
| const bool token_binding_enabled_; |
| uint16_t min_version_; |
| uint16_t fallback_min_version_; |
| }; |
| |
| // TODO(svaldez): Update tests to use EmbeddedTestServer. |
| #if !defined(OS_IOS) |
| class TokenBindingURLRequestTest : public URLRequestTestHTTP { |
| public: |
| void SetUp() override { |
| default_context_.set_ssl_config_service( |
| new TestSSLConfigService(false, false, false, true)); |
| channel_id_service_.reset(new ChannelIDService( |
| new DefaultChannelIDStore(NULL), base::ThreadTaskRunnerHandle::Get())); |
| default_context_.set_channel_id_service(channel_id_service_.get()); |
| URLRequestTestHTTP::SetUp(); |
| } |
| |
| protected: |
| scoped_ptr<ChannelIDService> channel_id_service_; |
| }; |
| |
| TEST_F(TokenBindingURLRequestTest, TokenBindingTest) { |
| SpawnedTestServer::SSLOptions ssl_options; |
| ssl_options.supported_token_binding_params.push_back(TB_PARAM_ECDSAP256); |
| SpawnedTestServer https_test_server(SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| https_test_server.GetURL("tokbind-ekm"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| |
| HttpRequestHeaders headers; |
| std::string token_binding_header, token_binding_message; |
| EXPECT_TRUE(r->GetFullRequestHeaders(&headers)); |
| EXPECT_TRUE(headers.GetHeader(HttpRequestHeaders::kTokenBinding, |
| &token_binding_header)); |
| EXPECT_TRUE(base::Base64UrlDecode( |
| token_binding_header, base::Base64UrlDecodePolicy::REQUIRE_PADDING, |
| &token_binding_message)); |
| base::StringPiece ec_point, signature; |
| EXPECT_TRUE( |
| ParseTokenBindingMessage(token_binding_message, &ec_point, &signature)); |
| |
| EXPECT_GT(d.bytes_received(), 0); |
| std::string ekm = d.data_received(); |
| |
| EXPECT_TRUE(VerifyEKMSignature(ec_point, signature, ekm)); |
| } |
| } |
| #endif // !defined(OS_IOS) |
| |
| // In this unit test, we're using the HTTPTestServer as a proxy server and |
| // issuing a CONNECT request with the magic host name "www.redirect.com". |
| // The EmbeddedTestServer will return a 302 response, which we should not |
| // follow. |
| TEST_F(URLRequestTestHTTP, ProxyTunnelRedirectTest) { |
| http_test_server()->RegisterRequestHandler( |
| base::Bind(&HandleRedirectConnect)); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| GURL("https://www.redirect.com/"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| // The proxy server is not set before failure. |
| EXPECT_TRUE(r->proxy_server().IsEmpty()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, r->status().error()); |
| EXPECT_EQ(1, d.response_started_count()); |
| // We should not have followed the redirect. |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| } |
| |
| // This is the same as the previous test, but checks that the network delegate |
| // registers the error. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateTunnelConnectionFailed) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| GURL("https://www.redirect.com/"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| // The proxy server is not set before failure. |
| EXPECT_TRUE(r->proxy_server().IsEmpty()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, r->status().error()); |
| EXPECT_EQ(1, d.response_started_count()); |
| // We should not have followed the redirect. |
| EXPECT_EQ(0, d.received_redirect_count()); |
| |
| EXPECT_EQ(1, network_delegate.error_count()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, network_delegate.last_error()); |
| } |
| } |
| |
| // Tests that we can block and asynchronously return OK in various stages. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateBlockAsynchronously) { |
| static const BlockingNetworkDelegate::Stage blocking_stages[] = { |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED |
| }; |
| static const size_t blocking_stages_length = arraysize(blocking_stages); |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::USER_CALLBACK); |
| network_delegate.set_block_on( |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST | |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS | |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| for (size_t i = 0; i < blocking_stages_length; ++i) { |
| base::RunLoop().Run(); |
| EXPECT_EQ(blocking_stages[i], |
| network_delegate.stage_blocked_for_callback()); |
| network_delegate.DoCallback(OK); |
| } |
| base::RunLoop().Run(); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can block and cancel a request. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| network_delegate.set_retval(ERR_EMPTY_RESPONSE); |
| |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| // The proxy server is not set before cancellation. |
| EXPECT_TRUE(r->proxy_server().IsEmpty()); |
| EXPECT_EQ(ERR_EMPTY_RESPONSE, r->status().error()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Helper function for NetworkDelegateCancelRequestAsynchronously and |
| // NetworkDelegateCancelRequestSynchronously. Sets up a blocking network |
| // delegate operating in |block_mode| and a request for |url|. It blocks the |
| // request in |stage| and cancels it with ERR_BLOCKED_BY_CLIENT. |
| void NetworkDelegateCancelRequest(BlockingNetworkDelegate::BlockMode block_mode, |
| BlockingNetworkDelegate::Stage stage, |
| const GURL& url) { |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate(block_mode); |
| network_delegate.set_retval(ERR_BLOCKED_BY_CLIENT); |
| network_delegate.set_block_on(stage); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| // The proxy server is not set before cancellation. |
| EXPECT_TRUE(r->proxy_server().IsEmpty()); |
| EXPECT_EQ(ERR_BLOCKED_BY_CLIENT, r->status().error()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // The following 3 tests check that the network delegate can cancel a request |
| // synchronously in various stages of the request. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously1) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED, |
| http_test_server()->GetURL("/")); |
| } |
| |
| // The following 3 tests check that the network delegate can cancel a request |
| // asynchronously in various stages of the request. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously1) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED, |
| http_test_server()->GetURL("/")); |
| } |
| |
| // Tests that the network delegate can block and redirect a request to a new |
| // URL. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| GURL redirect_url(http_test_server()->GetURL("/simple.html")); |
| network_delegate.set_redirect_url(redirect_url); |
| |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| { |
| GURL original_url(http_test_server()->GetURL("/defaultresponse")); |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| // Quit after hitting the redirect, so can check the headers. |
| d.set_quit_on_redirect(true); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| // Check headers from URLRequestJob. |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(307, r->GetResponseCode()); |
| EXPECT_EQ(307, r->response_headers()->response_code()); |
| std::string location; |
| ASSERT_TRUE(r->response_headers()->EnumerateHeader(NULL, "Location", |
| &location)); |
| EXPECT_EQ(redirect_url, GURL(location)); |
| |
| // Let the request finish. |
| r->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_TRUE(r->proxy_server().Equals(http_test_server()->host_port_pair())); |
| EXPECT_EQ( |
| 1, network_delegate.observed_before_proxy_headers_sent_callbacks()); |
| EXPECT_TRUE(network_delegate.last_observed_proxy().Equals( |
| http_test_server()->host_port_pair())); |
| |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can block and redirect a request to a new |
| // URL by setting a redirect_url and returning in OnBeforeURLRequest directly. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestSynchronously) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| GURL redirect_url(http_test_server()->GetURL("/simple.html")); |
| network_delegate.set_redirect_url(redirect_url); |
| |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| { |
| GURL original_url(http_test_server()->GetURL("/defaultresponse")); |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| // Quit after hitting the redirect, so can check the headers. |
| d.set_quit_on_redirect(true); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| // Check headers from URLRequestJob. |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(307, r->GetResponseCode()); |
| EXPECT_EQ(307, r->response_headers()->response_code()); |
| std::string location; |
| ASSERT_TRUE(r->response_headers()->EnumerateHeader(NULL, "Location", |
| &location)); |
| EXPECT_EQ(redirect_url, GURL(location)); |
| |
| // Let the request finish. |
| r->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_TRUE(r->proxy_server().Equals(http_test_server()->host_port_pair())); |
| EXPECT_EQ( |
| 1, network_delegate.observed_before_proxy_headers_sent_callbacks()); |
| EXPECT_TRUE(network_delegate.last_observed_proxy().Equals( |
| http_test_server()->host_port_pair())); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that redirects caused by the network delegate preserve POST data. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestPost) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| GURL redirect_url(http_test_server()->GetURL("/echo")); |
| network_delegate.set_redirect_url(redirect_url); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| GURL original_url(http_test_server()->GetURL("/defaultresponse")); |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| r->set_method("POST"); |
| r->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::SizeTToString(arraysize(kData) - 1)); |
| r->SetExtraRequestHeaders(headers); |
| |
| // Quit after hitting the redirect, so can check the headers. |
| d.set_quit_on_redirect(true); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| // Check headers from URLRequestJob. |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(307, r->GetResponseCode()); |
| EXPECT_EQ(307, r->response_headers()->response_code()); |
| std::string location; |
| ASSERT_TRUE(r->response_headers()->EnumerateHeader(NULL, "Location", |
| &location)); |
| EXPECT_EQ(redirect_url, GURL(location)); |
| |
| // Let the request finish. |
| r->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| EXPECT_EQ("POST", r->method()); |
| EXPECT_EQ(kData, d.data_received()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can block and redirect a request to a new |
| // URL during OnHeadersReceived. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestOnHeadersReceived) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED); |
| GURL redirect_url(http_test_server()->GetURL("/simple.html")); |
| network_delegate.set_redirect_on_headers_received_url(redirect_url); |
| |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| { |
| GURL original_url(http_test_server()->GetURL("/defaultresponse")); |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_TRUE(r->proxy_server().Equals(http_test_server()->host_port_pair())); |
| EXPECT_EQ( |
| 2, network_delegate.observed_before_proxy_headers_sent_callbacks()); |
| EXPECT_TRUE(network_delegate.last_observed_proxy().Equals( |
| http_test_server()->host_port_pair())); |
| |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(2, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can synchronously complete OnAuthRequired |
| // by taking no action. This indicates that the NetworkDelegate does not want to |
| // handle the challenge, and is passing the buck along to the |
| // URLRequest::Delegate. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredSyncNoAction) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_TRUE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, |
| NetworkDelegateOnAuthRequiredSyncNoAction_GetFullRequestHeaders) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| { |
| HttpRequestHeaders headers; |
| EXPECT_TRUE(r->GetFullRequestHeaders(&headers)); |
| EXPECT_FALSE(headers.HasHeader("Authorization")); |
| } |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_TRUE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can synchronously complete OnAuthRequired |
| // by setting credentials. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredSyncSetAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| network_delegate.set_auth_retval( |
| NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH); |
| |
| network_delegate.set_auth_credentials(AuthCredentials(kUser, kSecret)); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_FALSE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Same as above, but also tests that GetFullRequestHeaders returns the proper |
| // headers (for the first or second request) when called at the proper times. |
| TEST_F(URLRequestTestHTTP, |
| NetworkDelegateOnAuthRequiredSyncSetAuth_GetFullRequestHeaders) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| network_delegate.set_auth_retval( |
| NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH); |
| |
| network_delegate.set_auth_credentials(AuthCredentials(kUser, kSecret)); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_FALSE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| |
| { |
| HttpRequestHeaders headers; |
| EXPECT_TRUE(r->GetFullRequestHeaders(&headers)); |
| EXPECT_TRUE(headers.HasHeader("Authorization")); |
| } |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can synchronously complete OnAuthRequired |
| // by cancelling authentication. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredSyncCancel) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| network_delegate.set_auth_retval( |
| NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(401, r->GetResponseCode()); |
| EXPECT_FALSE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can asynchronously complete OnAuthRequired |
| // by taking no action. This indicates that the NetworkDelegate does not want |
| // to handle the challenge, and is passing the buck along to the |
| // URLRequest::Delegate. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredAsyncNoAction) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_TRUE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can asynchronously complete OnAuthRequired |
| // by setting credentials. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredAsyncSetAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| network_delegate.set_auth_retval( |
| NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH); |
| |
| AuthCredentials auth_credentials(kUser, kSecret); |
| network_delegate.set_auth_credentials(auth_credentials); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(0, r->status().error()); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_FALSE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can asynchronously complete OnAuthRequired |
| // by cancelling authentication. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredAsyncCancel) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::AUTO_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| network_delegate.set_auth_retval( |
| NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| scoped_ptr<URLRequest> r(context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(401, r->GetResponseCode()); |
| EXPECT_FALSE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 1: Request is cancelled while waiting for OnBeforeURLRequest callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting1) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::USER_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| EXPECT_EQ(ERR_ABORTED, r->status().error()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 2: Request is cancelled while waiting for OnBeforeSendHeaders callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::USER_CALLBACK); |
| network_delegate.set_block_on( |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| EXPECT_EQ(ERR_ABORTED, r->status().error()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 3: Request is cancelled while waiting for OnHeadersReceived callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::USER_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_HEADERS_RECEIVED, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| EXPECT_EQ(ERR_ABORTED, r->status().error()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 4: Request is cancelled while waiting for OnAuthRequired callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting4) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::USER_CALLBACK); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_AUTH_REQUIRED); |
| |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_AUTH_REQUIRED, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| EXPECT_EQ(ERR_ABORTED, r->status().error()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| namespace { |
| |
| scoped_ptr<test_server::HttpResponse> HandleServerAuthConnect( |
| const test_server::HttpRequest& request) { |
| if (request.headers.find("Host") == request.headers.end() || |
| request.headers.at("Host") != "www.server-auth.com" || |
| request.method != test_server::METHOD_CONNECT) { |
| return nullptr; |
| } |
| |
| scoped_ptr<test_server::BasicHttpResponse> http_response( |
| new test_server::BasicHttpResponse); |
| http_response->set_code(HTTP_UNAUTHORIZED); |
| http_response->AddCustomHeader("WWW-Authenticate", |
| "Basic realm=\"WallyWorld\""); |
| return std::move(http_response); |
| } |
| |
| } // namespace |
| |
| // In this unit test, we're using the EmbeddedTestServer as a proxy server and |
| // issuing a CONNECT request with the magic host name "www.server-auth.com". |
| // The EmbeddedTestServer will return a 401 response, which we should balk at. |
| TEST_F(URLRequestTestHTTP, UnexpectedServerAuthTest) { |
| http_test_server()->RegisterRequestHandler( |
| base::Bind(&HandleServerAuthConnect)); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContextWithProxy context( |
| http_test_server()->host_port_pair().ToString(), &network_delegate); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| GURL("https://www.server-auth.com/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| // The proxy server is not set before failure. |
| EXPECT_TRUE(r->proxy_server().IsEmpty()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, r->status().error()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTest_NoCache) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| |
| // TODO(eroman): Add back the NetLog tests... |
| } |
| } |
| |
| // This test has the server send a large number of cookies to the client. |
| // To ensure that no number of cookies causes a crash, a galloping binary |
| // search is used to estimate that maximum number of cookies that are accepted |
| // by the browser. Beyond the maximum number, the request will fail with |
| // ERR_RESPONSE_HEADERS_TOO_BIG. |
| #if defined(OS_WIN) |
| // http://crbug.com/177916 |
| #define MAYBE_GetTest_ManyCookies DISABLED_GetTest_ManyCookies |
| #else |
| #define MAYBE_GetTest_ManyCookies GetTest_ManyCookies |
| #endif // defined(OS_WIN) |
| TEST_F(URLRequestTestHTTP, MAYBE_GetTest_ManyCookies) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| int lower_bound = 0; |
| int upper_bound = 1; |
| |
| // Double the number of cookies until the response header limits are |
| // exceeded. |
| while (DoManyCookiesRequest(upper_bound)) { |
| lower_bound = upper_bound; |
| upper_bound *= 2; |
| ASSERT_LT(upper_bound, 1000000); |
| } |
| |
| int tolerance = static_cast<int>(upper_bound * 0.005); |
| if (tolerance < 2) |
| tolerance = 2; |
| |
| // Perform a binary search to find the highest possible number of cookies, |
| // within the desired tolerance. |
| while (upper_bound - lower_bound >= tolerance) { |
| int num_cookies = (lower_bound + upper_bound) / 2; |
| |
| if (DoManyCookiesRequest(num_cookies)) |
| lower_bound = num_cookies; |
| else |
| upper_bound = num_cookies; |
| } |
| // Success: the test did not crash. |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTest_GetFullRequestHeaders) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| GURL test_url(http_test_server()->GetURL("/defaultresponse")); |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| |
| HttpRequestHeaders headers; |
| EXPECT_FALSE(r->GetFullRequestHeaders(&headers)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| |
| EXPECT_TRUE(d.have_full_request_headers()); |
| CheckFullRequestHeaders(d.full_request_headers(), test_url); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTestLoadTiming) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| } |
| } |
| |
| // TODO(svaldez): Update tests to use EmbeddedTestServer. |
| #if !defined(OS_IOS) |
| TEST_F(URLRequestTestHTTP, GetZippedTest) { |
| SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTP, |
| SpawnedTestServer::kLocalhost, |
| base::FilePath(kTestFilePath)); |
| |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Parameter that specifies the Content-Length field in the response: |
| // C - Compressed length. |
| // U - Uncompressed length. |
| // L - Large length (larger than both C & U). |
| // M - Medium length (between C & U). |
| // S - Small length (smaller than both C & U). |
| const char test_parameters[] = "CULMS"; |
| const int num_tests = arraysize(test_parameters)- 1; // Skip NULL. |
| // C & U should be OK. |
| // L & M are larger than the data sent, and show an error. |
| // S has too little data, but we seem to accept it. |
| const bool test_expect_success[num_tests] = |
| { true, true, false, false, true }; |
| |
| for (int i = 0; i < num_tests ; i++) { |
| TestDelegate d; |
| { |
| std::string test_file = base::StringPrintf( |
| "compressedfiles/BullRunSpeech.txt?%c", test_parameters[i]); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| test_server.GetURL(test_file), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| VLOG(1) << " Received " << d.bytes_received() << " bytes" |
| << " status = " << r->status().status() |
| << " error = " << r->status().error(); |
| if (test_expect_success[i]) { |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()) |
| << " Parameter = \"" << test_file << "\""; |
| } else { |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| EXPECT_EQ(ERR_CONTENT_LENGTH_MISMATCH, r->status().error()) |
| << " Parameter = \"" << test_file << "\""; |
| } |
| } |
| } |
| } |
| #endif // !defined(OS_IOS) |
| |
| TEST_F(URLRequestTestHTTP, NetworkQualityEstimator) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| // Enable requests to local host to be used for network quality estimation. |
| std::map<std::string, std::string> variation_params; |
| NetworkQualityEstimator estimator(scoped_ptr<net::ExternalEstimateProvider>(), |
| variation_params, true, true); |
| |
| TestDelegate d; |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| std::string url = "/defaultresponse"; |
| |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL(url), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| EXPECT_TRUE(estimator.GetRTTEstimate(&rtt)); |
| EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps)); |
| EXPECT_GE(rtt, base::TimeDelta()); |
| EXPECT_LT(rtt, base::TimeDelta::Max()); |
| EXPECT_GT(kbps, 0); |
| |
| // Verify that histograms are not populated. They should populate only when |
| // there is a change in ConnectionType. |
| base::HistogramTester histogram_tester; |
| histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 0); |
| histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 0); |
| |
| NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); |
| base::MessageLoop::current()->RunUntilIdle(); |
| histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1); |
| histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectLoadTiming) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = http_test_server()->GetURL("/"); |
| GURL original_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(destination_url, req->url()); |
| EXPECT_EQ(original_url, req->original_url()); |
| ASSERT_EQ(2U, req->url_chain().size()); |
| EXPECT_EQ(original_url, req->url_chain()[0]); |
| EXPECT_EQ(destination_url, req->url_chain()[1]); |
| |
| LoadTimingInfo load_timing_info_before_redirect; |
| EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfoBeforeRedirect( |
| &load_timing_info_before_redirect)); |
| TestLoadTimingNotReused(load_timing_info_before_redirect, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| // Check that a new socket was used on redirect, since the server does not |
| // supposed keep-alive sockets, and that the times before the redirect are |
| // before the ones recorded for the second request. |
| EXPECT_NE(load_timing_info_before_redirect.socket_log_id, |
| load_timing_info.socket_log_id); |
| EXPECT_LE(load_timing_info_before_redirect.receive_headers_end, |
| load_timing_info.connect_timing.connect_start); |
| } |
| |
| TEST_F(URLRequestTestHTTP, MultipleRedirectTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = http_test_server()->GetURL("/"); |
| GURL middle_redirect_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| GURL original_url = http_test_server()->GetURL("/server-redirect?" + |
| middle_redirect_url.spec()); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(2, d.received_redirect_count()); |
| EXPECT_EQ(destination_url, req->url()); |
| EXPECT_EQ(original_url, req->original_url()); |
| ASSERT_EQ(3U, req->url_chain().size()); |
| EXPECT_EQ(original_url, req->url_chain()[0]); |
| EXPECT_EQ(middle_redirect_url, req->url_chain()[1]); |
| EXPECT_EQ(destination_url, req->url_chain()[2]); |
| } |
| |
| // First and second pieces of information logged by delegates to URLRequests. |
| const char kFirstDelegateInfo[] = "Wonderful delegate"; |
| const char kSecondDelegateInfo[] = "Exciting delegate"; |
| |
| // Logs delegate information to a URLRequest. The first string is logged |
| // synchronously on Start(), using DELEGATE_INFO_DEBUG_ONLY. The second is |
| // logged asynchronously, using DELEGATE_INFO_DISPLAY_TO_USER. Then |
| // another asynchronous call is used to clear the delegate information |
| // before calling a callback. The object then deletes itself. |
| class AsyncDelegateLogger : public base::RefCounted<AsyncDelegateLogger> { |
| public: |
| typedef base::Callback<void()> Callback; |
| |
| // Each time delegate information is added to the URLRequest, the resulting |
| // load state is checked. The expected load state after each request is |
| // passed in as an argument. |
| static void Run(URLRequest* url_request, |
| LoadState expected_first_load_state, |
| LoadState expected_second_load_state, |
| LoadState expected_third_load_state, |
| const Callback& callback) { |
| AsyncDelegateLogger* logger = new AsyncDelegateLogger( |
| url_request, |
| expected_first_load_state, |
| expected_second_load_state, |
| expected_third_load_state, |
| callback); |
| logger->Start(); |
| } |
| |
| // Checks that the log entries, starting with log_position, contain the |
| // DELEGATE_INFO NetLog events that an AsyncDelegateLogger should have |
| // recorded. Returns the index of entry after the expected number of |
| // events this logged, or entries.size() if there aren't enough entries. |
| static size_t CheckDelegateInfo(const TestNetLogEntry::List& entries, |
| size_t log_position) { |
| // There should be 4 DELEGATE_INFO events: Two begins and two ends. |
| if (log_position + 3 >= entries.size()) { |
| ADD_FAILURE() << "Not enough log entries"; |
| return entries.size(); |
| } |
| std::string delegate_info; |
| EXPECT_EQ(NetLog::TYPE_DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_BEGIN, entries[log_position].phase); |
| EXPECT_TRUE(entries[log_position].GetStringValue("delegate_info", |
| &delegate_info)); |
| EXPECT_EQ(kFirstDelegateInfo, delegate_info); |
| |
| ++log_position; |
| EXPECT_EQ(NetLog::TYPE_DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| |
| ++log_position; |
| EXPECT_EQ(NetLog::TYPE_DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_BEGIN, entries[log_position].phase); |
| EXPECT_TRUE(entries[log_position].GetStringValue("delegate_info", |
| &delegate_info)); |
| EXPECT_EQ(kSecondDelegateInfo, delegate_info); |
| |
| ++log_position; |
| EXPECT_EQ(NetLog::TYPE_DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| |
| return log_position + 1; |
| } |
| |
| // Find delegate request begin and end messages for OnBeforeNetworkStart. |
| // Returns the position of the end message. |
| static size_t ExpectBeforeNetworkEvents(const TestNetLogEntry::List& entries, |
| size_t log_position) { |
| log_position = |
| ExpectLogContainsSomewhereAfter(entries, |
| log_position, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, |
| entries[log_position + 1].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position + 1].phase); |
| return log_position + 1; |
| } |
| |
| private: |
| friend class base::RefCounted<AsyncDelegateLogger>; |
| |
| AsyncDelegateLogger(URLRequest* url_request, |
| LoadState expected_first_load_state, |
| LoadState expected_second_load_state, |
| LoadState expected_third_load_state, |
| const Callback& callback) |
| : url_request_(url_request), |
| expected_first_load_state_(expected_first_load_state), |
| expected_second_load_state_(expected_second_load_state), |
| expected_third_load_state_(expected_third_load_state), |
| callback_(callback) { |
| } |
| |
| ~AsyncDelegateLogger() {} |
| |
| void Start() { |
| url_request_->LogBlockedBy(kFirstDelegateInfo); |
| LoadStateWithParam load_state = url_request_->GetLoadState(); |
| EXPECT_EQ(expected_first_load_state_, load_state.state); |
| EXPECT_NE(ASCIIToUTF16(kFirstDelegateInfo), load_state.param); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::Bind(&AsyncDelegateLogger::LogSecondDelegate, this)); |
| } |
| |
| void LogSecondDelegate() { |
| url_request_->LogAndReportBlockedBy(kSecondDelegateInfo); |
| LoadStateWithParam load_state = url_request_->GetLoadState(); |
| EXPECT_EQ(expected_second_load_state_, load_state.state); |
| if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE) { |
| EXPECT_EQ(ASCIIToUTF16(kSecondDelegateInfo), load_state.param); |
| } else { |
| EXPECT_NE(ASCIIToUTF16(kSecondDelegateInfo), load_state.param); |
| } |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::Bind(&AsyncDelegateLogger::LogComplete, this)); |
| } |
| |
| void LogComplete() { |
| url_request_->LogUnblocked(); |
| LoadStateWithParam load_state = url_request_->GetLoadState(); |
| EXPECT_EQ(expected_third_load_state_, load_state.state); |
| if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE) |
| EXPECT_EQ(base::string16(), load_state.param); |
| callback_.Run(); |
| } |
| |
| URLRequest* url_request_; |
| const int expected_first_load_state_; |
| const int expected_second_load_state_; |
| const int expected_third_load_state_; |
| const Callback callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AsyncDelegateLogger); |
| }; |
| |
| // NetworkDelegate that logs delegate information before a request is started, |
| // before headers are sent, when headers are read, and when auth information |
| // is requested. Uses AsyncDelegateLogger. |
| class AsyncLoggingNetworkDelegate : public TestNetworkDelegate { |
| public: |
| AsyncLoggingNetworkDelegate() {} |
| ~AsyncLoggingNetworkDelegate() override {} |
| |
| // NetworkDelegate implementation. |
| int OnBeforeURLRequest(URLRequest* request, |
| const CompletionCallback& callback, |
| GURL* new_url) override { |
| TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url); |
| return RunCallbackAsynchronously(request, callback); |
| } |
| |
| int OnBeforeSendHeaders(URLRequest* request, |
| const CompletionCallback& callback, |
| HttpRequestHeaders* headers) override { |
| TestNetworkDelegate::OnBeforeSendHeaders(request, callback, headers); |
| return RunCallbackAsynchronously(request, callback); |
| } |
| |
| int OnHeadersReceived( |
| URLRequest* request, |
| const CompletionCallback& callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| GURL* allowed_unsafe_redirect_url) override { |
| TestNetworkDelegate::OnHeadersReceived(request, |
| callback, |
| original_response_headers, |
| override_response_headers, |
| allowed_unsafe_redirect_url); |
| return RunCallbackAsynchronously(request, callback); |
| } |
| |
| NetworkDelegate::AuthRequiredResponse OnAuthRequired( |
| URLRequest* request, |
| const AuthChallengeInfo& auth_info, |
| const AuthCallback& callback, |
| AuthCredentials* credentials) override { |
| AsyncDelegateLogger::Run( |
| request, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::Bind(&AsyncLoggingNetworkDelegate::SetAuthAndResume, |
| callback, credentials)); |
| return AUTH_REQUIRED_RESPONSE_IO_PENDING; |
| } |
| |
| private: |
| static int RunCallbackAsynchronously( |
| URLRequest* request, |
| const CompletionCallback& callback) { |
| AsyncDelegateLogger::Run( |
| request, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::Bind(callback, OK)); |
| return ERR_IO_PENDING; |
| } |
| |
| static void SetAuthAndResume(const AuthCallback& callback, |
| AuthCredentials* credentials) { |
| *credentials = AuthCredentials(kUser, kSecret); |
| callback.Run(NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(AsyncLoggingNetworkDelegate); |
| }; |
| |
| // URLRequest::Delegate that logs delegate information when the headers |
| // are received, when each read completes, and during redirects. Uses |
| // AsyncDelegateLogger. Can optionally cancel a request in any phase. |
| // |
| // Inherits from TestDelegate to reuse the TestDelegate code to handle |
| // advancing to the next step in most cases, as well as cancellation. |
| class AsyncLoggingUrlRequestDelegate : public TestDelegate { |
| public: |
| enum CancelStage { |
| NO_CANCEL = 0, |
| CANCEL_ON_RECEIVED_REDIRECT, |
| CANCEL_ON_RESPONSE_STARTED, |
| CANCEL_ON_READ_COMPLETED |
| }; |
| |
| explicit AsyncLoggingUrlRequestDelegate(CancelStage cancel_stage) |
| : cancel_stage_(cancel_stage) { |
| if (cancel_stage == CANCEL_ON_RECEIVED_REDIRECT) |
| set_cancel_in_received_redirect(true); |
| else if (cancel_stage == CANCEL_ON_RESPONSE_STARTED) |
| set_cancel_in_response_started(true); |
| else if (cancel_stage == CANCEL_ON_READ_COMPLETED) |
| set_cancel_in_received_data(true); |
| } |
| ~AsyncLoggingUrlRequestDelegate() override {} |
| |
| // URLRequest::Delegate implementation: |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| *defer_redirect = true; |
| AsyncDelegateLogger::Run( |
| request, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::Bind( |
| &AsyncLoggingUrlRequestDelegate::OnReceivedRedirectLoggingComplete, |
| base::Unretained(this), request, redirect_info)); |
| } |
| |
| void OnResponseStarted(URLRequest* request) override { |
| AsyncDelegateLogger::Run( |
| request, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::Bind( |
| &AsyncLoggingUrlRequestDelegate::OnResponseStartedLoggingComplete, |
| base::Unretained(this), request)); |
| } |
| |
| void OnReadCompleted(URLRequest* request, int bytes_read) override { |
| AsyncDelegateLogger::Run( |
| request, |
| LOAD_STATE_IDLE, |
| LOAD_STATE_IDLE, |
| LOAD_STATE_IDLE, |
| base::Bind( |
| &AsyncLoggingUrlRequestDelegate::AfterReadCompletedLoggingComplete, |
| base::Unretained(this), request, bytes_read)); |
| } |
| |
| private: |
| void OnReceivedRedirectLoggingComplete(URLRequest* request, |
| const RedirectInfo& redirect_info) { |
| bool defer_redirect = false; |
| TestDelegate::OnReceivedRedirect(request, redirect_info, &defer_redirect); |
| // FollowDeferredRedirect should not be called after cancellation. |
| if (cancel_stage_ == CANCEL_ON_RECEIVED_REDIRECT) |
| return; |
| if (!defer_redirect) |
| request->FollowDeferredRedirect(); |
| } |
| |
| void OnResponseStartedLoggingComplete(URLRequest* request) { |
| // The parent class continues the request. |
| TestDelegate::OnResponseStarted(request); |
| } |
| |
| void AfterReadCompletedLoggingComplete(URLRequest* request, int bytes_read) { |
| // The parent class continues the request. |
| TestDelegate::OnReadCompleted(request, bytes_read); |
| } |
| |
| const CancelStage cancel_stage_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AsyncLoggingUrlRequestDelegate); |
| }; |
| |
| // Tests handling of delegate info before a request starts. |
| TEST_F(URLRequestTestHTTP, DelegateInfoBeforeStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(NULL); |
| context.set_net_log(&net_log_); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(http_test_server()->GetURL("/defaultresponse"), |
| DEFAULT_PRIORITY, &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(base::string16(), load_state.param); |
| |
| AsyncDelegateLogger::Run( |
| r.get(), |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_IDLE, |
| base::Bind(&URLRequest::Start, base::Unretained(r.get()))); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| } |
| |
| TestNetLogEntry::List entries; |
| net_log_.GetEntries(&entries); |
| size_t log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| 0, |
| NetLog::TYPE_DELEGATE_INFO, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position); |
| |
| // Nothing else should add any delegate info to the request. |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a network delegate. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateInfo) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| AsyncLoggingNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_net_log(&net_log_); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(http_test_server()->GetURL("/simple.html"), |
| DEFAULT_PRIORITY, &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(base::string16(), load_state.param); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| |
| size_t log_position = 0; |
| TestNetLogEntry::List entries; |
| net_log_.GetEntries(&entries); |
| for (size_t i = 0; i < 3; ++i) { |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position + 1, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| |
| if (i == 1) { |
| log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents( |
| entries, log_position + 1); |
| } |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a network delegate in the case of an |
| // HTTP redirect. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateInfoRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| AsyncLoggingNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_net_log(&net_log_); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/server-redirect?simple.html"), |
| DEFAULT_PRIORITY, &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(base::string16(), load_state.param); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(2, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| |
| size_t log_position = 0; |
| TestNetLogEntry::List entries; |
| net_log_.GetEntries(&entries); |
| // The NetworkDelegate logged information in OnBeforeURLRequest, |
| // OnBeforeSendHeaders, and OnHeadersReceived. |
| for (size_t i = 0; i < 3; ++i) { |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position + 1, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| |
| if (i == 1) { |
| log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents( |
| entries, log_position + 1); |
| } |
| } |
| |
| // The URLRequest::Delegate then gets informed about the redirect. |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position + 1, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| // The NetworkDelegate logged information in the same three events as before. |
| for (size_t i = 0; i < 3; ++i) { |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position + 1, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a network delegate in the case of HTTP |
| // AUTH. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateInfoAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| AsyncLoggingNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_net_log(&net_log_); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(http_test_server()->GetURL("/auth-basic"), |
| DEFAULT_PRIORITY, &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(base::string16(), load_state.param); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| |
| size_t log_position = 0; |
| TestNetLogEntry::List entries; |
| net_log_.GetEntries(&entries); |
| // The NetworkDelegate should have logged information in OnBeforeURLRequest, |
| // OnBeforeSendHeaders, OnHeadersReceived, OnAuthRequired, and then again in |
| // OnBeforeURLRequest and OnBeforeSendHeaders. |
| for (size_t i = 0; i < 6; ++i) { |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position + 1, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| |
| if (i == 1) { |
| log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents( |
| entries, log_position + 1); |
| } |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| } |
| |
| // TODO(svaldez): Update tests to use EmbeddedTestServer. |
| #if !defined(OS_IOS) |
| // Tests handling of delegate info from a URLRequest::Delegate. |
| TEST_F(URLRequestTestHTTP, URLRequestDelegateInfo) { |
| SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTP, |
| SpawnedTestServer::kLocalhost, |
| base::FilePath(kTestFilePath)); |
| |
| ASSERT_TRUE(test_server.Start()); |
| |
| AsyncLoggingUrlRequestDelegate request_delegate( |
| AsyncLoggingUrlRequestDelegate::NO_CANCEL); |
| TestURLRequestContext context(true); |
| context.set_network_delegate(NULL); |
| context.set_net_log(&net_log_); |
| context.Init(); |
| |
| { |
| // A chunked response with delays between chunks is used to make sure that |
| // attempts by the URLRequest delegate to log information while reading the |
| // body are ignored. Since they are ignored, this test is robust against |
| // the possibility of multiple reads being combined in the unlikely event |
| // that it occurs. |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| test_server.GetURL("/chunked?waitBetweenChunks=20"), DEFAULT_PRIORITY, |
| &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| } |
| |
| TestNetLogEntry::List entries; |
| net_log_.GetEntries(&entries); |
| |
| size_t log_position = 0; |
| |
| log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents( |
| entries, log_position); |
| |
| // The delegate info should only have been logged on header complete. Other |
| // times it should silently be ignored. |
| log_position = |
| ExpectLogContainsSomewhereAfter(entries, |
| log_position + 1, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_URL_REQUEST_DELEGATE)); |
| } |
| #endif // !defined(OS_IOS) |
| |
| // Tests handling of delegate info from a URLRequest::Delegate in the case of |
| // an HTTP redirect. |
| TEST_F(URLRequestTestHTTP, URLRequestDelegateInfoOnRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| AsyncLoggingUrlRequestDelegate request_delegate( |
| AsyncLoggingUrlRequestDelegate::NO_CANCEL); |
| TestURLRequestContext context(true); |
| context.set_network_delegate(NULL); |
| context.set_net_log(&net_log_); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/server-redirect?simple.html"), |
| DEFAULT_PRIORITY, &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| } |
| |
| TestNetLogEntry::List entries; |
| net_log_.GetEntries(&entries); |
| |
| // Delegate info should only have been logged in OnReceivedRedirect and |
| // OnResponseStarted. |
| size_t log_position = 0; |
| for (int i = 0; i < 2; ++i) { |
| if (i == 0) { |
| log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents( |
| entries, log_position) + 1; |
| } |
| |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_URL_REQUEST_DELEGATE)); |
| } |
| |
| // Tests handling of delegate info from a URLRequest::Delegate in the case of |
| // an HTTP redirect, with cancellation at various points. |
| TEST_F(URLRequestTestHTTP, URLRequestDelegateOnRedirectCancelled) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const AsyncLoggingUrlRequestDelegate::CancelStage kCancelStages[] = { |
| AsyncLoggingUrlRequestDelegate::CANCEL_ON_RECEIVED_REDIRECT, |
| AsyncLoggingUrlRequestDelegate::CANCEL_ON_RESPONSE_STARTED, |
| AsyncLoggingUrlRequestDelegate::CANCEL_ON_READ_COMPLETED, |
| }; |
| |
| for (size_t test_case = 0; test_case < arraysize(kCancelStages); |
| ++test_case) { |
| AsyncLoggingUrlRequestDelegate request_delegate(kCancelStages[test_case]); |
| TestURLRequestContext context(true); |
| TestNetLog net_log; |
| context.set_network_delegate(NULL); |
| context.set_net_log(&net_log); |
| context.Init(); |
| |
| { |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| http_test_server()->GetURL("/server-redirect?simple.html"), |
| DEFAULT_PRIORITY, &request_delegate)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| } |
| |
| TestNetLogEntry::List entries; |
| net_log.GetEntries(&entries); |
| |
| // Delegate info is always logged in both OnReceivedRedirect and |
| // OnResponseStarted. In the CANCEL_ON_RECEIVED_REDIRECT, the |
| // OnResponseStarted delegate call is after cancellation, but logging is |
| // still currently supported in that call. |
| size_t log_position = 0; |
| for (int i = 0; i < 2; ++i) { |
| if (i == 0) { |
| log_position = AsyncDelegateLogger::ExpectBeforeNetworkEvents( |
| entries, log_position) + 1; |
| } |
| |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, |
| log_position, |
| NetLog::TYPE_URL_REQUEST_DELEGATE, |
| NetLog::PHASE_BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, |
| log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLog::TYPE_URL_REQUEST_DELEGATE, entries[log_position].type); |
| EXPECT_EQ(NetLog::PHASE_END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_DELEGATE_INFO)); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, NetLog::TYPE_URL_REQUEST_DELEGATE)); |
| } |
| } |
| |
| namespace { |
| |
| const char kExtraHeader[] = "Allow-Snafu"; |
| const char kExtraValue[] = "fubar"; |
| |
| class RedirectWithAdditionalHeadersDelegate : public TestDelegate { |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); |
| request->SetExtraRequestHeaderByName(kExtraHeader, kExtraValue, false); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST_F(URLRequestTestHTTP, RedirectWithAdditionalHeadersTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = |
| http_test_server()->GetURL("/echoheader?" + std::string(kExtraHeader)); |
| GURL original_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| RedirectWithAdditionalHeadersDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| std::string value; |
| const HttpRequestHeaders& headers = req->extra_request_headers(); |
| EXPECT_TRUE(headers.GetHeader(kExtraHeader, &value)); |
| EXPECT_EQ(kExtraValue, value); |
| EXPECT_FALSE(req->is_pending()); |
| EXPECT_FALSE(req->is_redirecting()); |
| EXPECT_EQ(kExtraValue, d.data_received()); |
| } |
| |
| namespace { |
| |
| const char kExtraHeaderToRemove[] = "To-Be-Removed"; |
| |
| class RedirectWithHeaderRemovalDelegate : public TestDelegate { |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); |
| request->RemoveRequestHeaderByName(kExtraHeaderToRemove); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST_F(URLRequestTestHTTP, RedirectWithHeaderRemovalTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = http_test_server()->GetURL( |
| "/echoheader?" + std::string(kExtraHeaderToRemove)); |
| GURL original_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| RedirectWithHeaderRemovalDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| req->SetExtraRequestHeaderByName(kExtraHeaderToRemove, "dummy", false); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| std::string value; |
| const HttpRequestHeaders& headers = req->extra_request_headers(); |
| EXPECT_FALSE(headers.GetHeader(kExtraHeaderToRemove, &value)); |
| EXPECT_FALSE(req->is_pending()); |
| EXPECT_FALSE(req->is_redirecting()); |
| EXPECT_EQ("None", d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelTest) { |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| GURL("http://www.google.com/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| r->Cancel(); |
| |
| base::RunLoop().Run(); |
| |
| // We expect to receive OnResponseStarted even though the request has been |
| // cancelled. |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelTest2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| d.set_cancel_in_response_started(true); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelTest3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| d.set_cancel_in_received_data(true); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| // There is no guarantee about how much data was received |
| // before the cancel was issued. It could have been 0 bytes, |
| // or it could have been all the bytes. |
| // EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelTest4) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| // The request will be implicitly canceled when it is destroyed. The |
| // test delegate must not post a quit message when this happens because |
| // this test doesn't actually have a message loop. The quit message would |
| // get put on this thread's message queue and the next test would exit |
| // early, causing problems. |
| d.set_quit_on_complete(false); |
| } |
| // expect things to just cleanup properly. |
| |
| // we won't actually get a received response here because we've never run the |
| // message loop |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelTest5) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // populate cache |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| } |
| |
| // cancel read from cache (see bug 990242) |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| r->Cancel(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::CANCELED, r->status().status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| HTTPUploadDataOperationTest("POST"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, PutTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| HTTPUploadDataOperationTest("PUT"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostEmptyTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->set_method("POST"); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| ASSERT_EQ(1, d.response_started_count()) |
| << "request failed: " << r->status().status() |
| << ", error: " << r->status().error(); |
| |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.data_received().empty()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostFileTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->set_method("POST"); |
| |
| base::FilePath dir; |
| PathService::Get(base::DIR_EXE, &dir); |
| base::SetCurrentDirectory(dir); |
| |
| std::vector<scoped_ptr<UploadElementReader>> element_readers; |
| |
| base::FilePath path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| path = path.Append(FILE_PATH_LITERAL("with-headers.html")); |
| element_readers.push_back(make_scoped_ptr(new UploadFileElementReader( |
| base::ThreadTaskRunnerHandle::Get().get(), path, 0, |
| std::numeric_limits<uint64_t>::max(), base::Time()))); |
| r->set_upload(make_scoped_ptr<UploadDataStream>( |
| new ElementsUploadDataStream(std::move(element_readers), 0))); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| int64_t size64 = 0; |
| ASSERT_EQ(true, base::GetFileSize(path, &size64)); |
| ASSERT_LE(size64, std::numeric_limits<int>::max()); |
| int size = static_cast<int>(size64); |
| scoped_ptr<char[]> buf(new char[size]); |
| |
| ASSERT_EQ(size, base::ReadFile(path, buf.get(), size)); |
| |
| ASSERT_EQ(1, d.response_started_count()) |
| << "request failed: " << r->status().status() |
| << ", error: " << r->status().error(); |
| |
| EXPECT_FALSE(d.received_data_before_response()); |
| |
| EXPECT_EQ(size, d.bytes_received()); |
| EXPECT_EQ(std::string(&buf[0], size), d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostUnreadableFileTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->set_method("POST"); |
| |
| std::vector<scoped_ptr<UploadElementReader>> element_readers; |
| |
| element_readers.push_back(make_scoped_ptr(new UploadFileElementReader( |
| base::ThreadTaskRunnerHandle::Get().get(), |
| base::FilePath(FILE_PATH_LITERAL( |
| "c:\\path\\to\\non\\existant\\file.randomness.12345")), |
| 0, std::numeric_limits<uint64_t>::max(), base::Time()))); |
| r->set_upload(make_scoped_ptr<UploadDataStream>( |
| new ElementsUploadDataStream(std::move(element_readers), 0))); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| EXPECT_EQ(ERR_FILE_NOT_FOUND, r->status().error()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, TestPostChunkedDataBeforeStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->EnableChunkedUpload(); |
| r->set_method("POST"); |
| AddChunksToUpload(r.get()); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| VerifyReceivedDataMatchesChunks(r.get(), &d); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, TestPostChunkedDataJustAfterStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->EnableChunkedUpload(); |
| r->set_method("POST"); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| AddChunksToUpload(r.get()); |
| base::RunLoop().Run(); |
| |
| VerifyReceivedDataMatchesChunks(r.get(), &d); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, TestPostChunkedDataAfterStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d)); |
| r->EnableChunkedUpload(); |
| r->set_method("POST"); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().RunUntilIdle(); |
| AddChunksToUpload(r.get()); |
| base::RunLoop().Run(); |
| |
| VerifyReceivedDataMatchesChunks(r.get(), &d); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, ResponseHeadersTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/with-headers.html"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| const HttpResponseHeaders* headers = req->response_headers(); |
| |
| // Simple sanity check that response_info() accesses the same data. |
| EXPECT_EQ(headers, req->response_info().headers.get()); |
| |
| std::string header; |
| EXPECT_TRUE(headers->GetNormalizedHeader("cache-control", &header)); |
| EXPECT_EQ("private", header); |
| |
| header.clear(); |
| EXPECT_TRUE(headers->GetNormalizedHeader("content-type", &header)); |
| EXPECT_EQ("text/html; charset=ISO-8859-1", header); |
| |
| // The response has two "X-Multiple-Entries" headers. |
| // This verfies our output has them concatenated together. |
| header.clear(); |
| EXPECT_TRUE(headers->GetNormalizedHeader("x-multiple-entries", &header)); |
| EXPECT_EQ("a, b", header); |
| } |
| |
| // TODO(svaldez): iOS tests are flaky with EmbeddedTestServer and transport |
| // security state. (see http://crbug.com/550977). |
| #if !defined(OS_IOS) |
| TEST_F(URLRequestTestHTTP, ProcessSTS) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hsts-headers.html"), DEFAULT_PRIORITY, &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_TRUE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_FALSE( |
| security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, |
| sts_state.upgrade_mode); |
| EXPECT_TRUE(sts_state.include_subdomains); |
| EXPECT_FALSE(pkp_state.include_subdomains); |
| #if defined(OS_ANDROID) |
| // Android's CertVerifyProc does not (yet) handle pins. |
| #else |
| EXPECT_FALSE(pkp_state.HasPublicKeyPins()); |
| #endif |
| } |
| |
| TEST_F(URLRequestTestHTTP, STSNotProcessedOnIP) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| // Make sure this test fails if the test server is changed to not |
| // listen on an IP by default. |
| ASSERT_TRUE(https_test_server.GetURL("/").HostIsIPAddress()); |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hsts-headers.html"), DEFAULT_PRIORITY, &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| EXPECT_FALSE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| } |
| |
| // Android's CertVerifyProc does not (yet) handle pins. Therefore, it will |
| // reject HPKP headers, and a test setting only HPKP headers will fail (no |
| // PKPState present because header rejected). |
| #if defined(OS_ANDROID) |
| #define MAYBE_ProcessPKP DISABLED_ProcessPKP |
| #define MAYBE_ProcessPKPAndSendReport DISABLED_ProcessPKPAndSendReport |
| #define MAYBE_ProcessPKPReportOnly DISABLED_ProcessPKPReportOnly |
| #define MAYBE_ProcessPKPReportOnlyWithNoViolation \ |
| DISABLED_ProcessPKPReportOnlyWithNoViolation |
| #else |
| #define MAYBE_ProcessPKP ProcessPKP |
| #define MAYBE_ProcessPKPAndSendReport ProcessPKPAndSendReport |
| #define MAYBE_ProcessPKPReportOnly ProcessPKPReportOnly |
| #define MAYBE_ProcessPKPReportOnlyWithNoViolation \ |
| ProcessPKPReportOnlyWithNoViolation |
| #endif |
| |
| namespace { |
| const char kExpectCTStaticHostname[] = "preloaded-expect-ct.badssl.com"; |
| const char kHPKPReportUri[] = "https://hpkp-report.test"; |
| } // namespace |
| |
| // Tests that enabling HPKP on a domain does not affect the HSTS |
| // validity/expiration. |
| TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKP) { |
| GURL report_uri(kHPKPReportUri); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hpkp-headers.html"), DEFAULT_PRIORITY, &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_FALSE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_TRUE( |
| security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_DEFAULT, |
| sts_state.upgrade_mode); |
| EXPECT_FALSE(sts_state.include_subdomains); |
| EXPECT_FALSE(pkp_state.include_subdomains); |
| EXPECT_TRUE(pkp_state.HasPublicKeyPins()); |
| EXPECT_EQ(report_uri, pkp_state.report_uri); |
| EXPECT_NE(sts_state.expiry, pkp_state.expiry); |
| } |
| |
| // Tests that reports get sent on HPKP violations when a report-uri is set. |
| TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKPAndSendReport) { |
| GURL report_uri(kHPKPReportUri); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| // Set up a pin for |test_server_hostname|. |
| TransportSecurityState security_state; |
| const base::Time current_time(base::Time::Now()); |
| const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); |
| HashValueVector hashes; |
| HashValue hash1; |
| HashValue hash2; |
| // The values here don't matter, as long as they are different from |
| // the mocked CertVerifyResult below. |
| ASSERT_TRUE( |
| hash1.FromString("sha256/1111111111111111111111111111111111111111111=")); |
| ASSERT_TRUE( |
| hash2.FromString("sha256/2222222222222222222222222222222222222222222=")); |
| hashes.push_back(hash1); |
| hashes.push_back(hash2); |
| security_state.AddHPKP(test_server_hostname, expiry, |
| false, /* include subdomains */ |
| hashes, report_uri); |
| |
| MockCertificateReportSender mock_report_sender; |
| security_state.SetReportSender(&mock_report_sender); |
| |
| // Set up a MockCertVerifier to trigger a violation of the previously |
| // set pin. |
| scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| |
| MockCertVerifier cert_verifier; |
| CertVerifyResult verify_result; |
| verify_result.verified_cert = cert; |
| verify_result.is_issued_by_known_root = true; |
| HashValue hash3; |
| ASSERT_TRUE( |
| hash3.FromString("sha256/3333333333333333333333333333333333333333333=")); |
| verify_result.public_key_hashes.push_back(hash3); |
| cert_verifier.AddResultForCert(cert.get(), verify_result, OK); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_transport_security_state(&security_state); |
| context.set_network_delegate(&network_delegate); |
| context.set_cert_verifier(&cert_verifier); |
| context.Init(); |
| |
| // Now send a request to trigger the violation. |
| TestDelegate d; |
| scoped_ptr<URLRequest> violating_request(context.CreateRequest( |
| https_test_server.GetURL("/simple.html"), DEFAULT_PRIORITY, &d)); |
| violating_request->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that a report was sent. |
| EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
| ASSERT_FALSE(mock_report_sender.latest_report().empty()); |
| scoped_ptr<base::Value> value( |
| base::JSONReader::Read(mock_report_sender.latest_report())); |
| ASSERT_TRUE(value); |
| ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); |
| base::DictionaryValue* report_dict; |
| ASSERT_TRUE(value->GetAsDictionary(&report_dict)); |
| std::string report_hostname; |
| EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname)); |
| EXPECT_EQ(test_server_hostname, report_hostname); |
| } |
| |
| // Tests that reports get sent on requests with |
| // Public-Key-Pins-Report-Only headers. |
| TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKPReportOnly) { |
| GURL report_uri(kHPKPReportUri); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TransportSecurityState security_state; |
| MockCertificateReportSender mock_report_sender; |
| security_state.SetReportSender(&mock_report_sender); |
| |
| // Set up a MockCertVerifier to violate the pin in the Report-Only |
| // header. |
| scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| |
| MockCertVerifier cert_verifier; |
| CertVerifyResult verify_result; |
| verify_result.verified_cert = cert; |
| verify_result.is_issued_by_known_root = true; |
| HashValue hash; |
| // This value doesn't matter, as long as it is different from the pins |
| // for the request to hpkp-headers-report-only.html. |
| ASSERT_TRUE( |
| hash.FromString("sha256/1111111111111111111111111111111111111111111=")); |
| verify_result.public_key_hashes.push_back(hash); |
| cert_verifier.AddResultForCert(cert.get(), verify_result, OK); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_transport_security_state(&security_state); |
| context.set_network_delegate(&network_delegate); |
| context.set_cert_verifier(&cert_verifier); |
| context.Init(); |
| |
| // Now send a request to trigger the violation. |
| TestDelegate d; |
| scoped_ptr<URLRequest> violating_request(context.CreateRequest( |
| https_test_server.GetURL("/hpkp-headers-report-only.html"), |
| DEFAULT_PRIORITY, &d)); |
| violating_request->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that a report was sent. |
| EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
| ASSERT_FALSE(mock_report_sender.latest_report().empty()); |
| scoped_ptr<base::Value> value( |
| base::JSONReader::Read(mock_report_sender.latest_report())); |
| ASSERT_TRUE(value); |
| ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); |
| base::DictionaryValue* report_dict; |
| ASSERT_TRUE(value->GetAsDictionary(&report_dict)); |
| std::string report_hostname; |
| EXPECT_TRUE(report_dict->GetString("hostname", &report_hostname)); |
| EXPECT_EQ(test_server_hostname, report_hostname); |
| } |
| |
| // Tests that reports do not get sent on requests with |
| // Public-Key-Pins-Report-Only headers that don't have pin violations. |
| TEST_F(URLRequestTestHTTP, MAYBE_ProcessPKPReportOnlyWithNoViolation) { |
| GURL report_uri(kHPKPReportUri); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TransportSecurityState security_state; |
| MockCertificateReportSender mock_report_sender; |
| security_state.SetReportSender(&mock_report_sender); |
| |
| TestNetworkDelegate network_delegate; |
| MockCertVerifier mock_cert_verifier; |
| TestURLRequestContext context(true); |
| context.set_transport_security_state(&security_state); |
| context.set_network_delegate(&network_delegate); |
| context.set_cert_verifier(&mock_cert_verifier); |
| mock_cert_verifier.set_default_result(OK); |
| context.Init(); |
| |
| // Now send a request that does not trigger the violation. |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(context.CreateRequest( |
| https_test_server.GetURL("/hpkp-headers-report-only.html"), |
| DEFAULT_PRIORITY, &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| |
| // Check that a report was not sent. |
| EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); |
| EXPECT_EQ(std::string(), mock_report_sender.latest_report()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, PKPNotProcessedOnIP) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| // Make sure this test fails if the test server is changed to not |
| // listen on an IP by default. |
| ASSERT_TRUE(https_test_server.GetURL("/").HostIsIPAddress()); |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hpkp-headers.html"), DEFAULT_PRIORITY, &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_FALSE( |
| security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); |
| } |
| |
| TEST_F(URLRequestTestHTTP, ProcessSTSOnce) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hsts-multiple-headers.html"), DEFAULT_PRIORITY, |
| &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| |
| // We should have set parameters from the first header, not the second. |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| EXPECT_TRUE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, |
| sts_state.upgrade_mode); |
| EXPECT_FALSE(sts_state.include_subdomains); |
| EXPECT_FALSE(sts_state.include_subdomains); |
| } |
| |
| TEST_F(URLRequestTestHTTP, ProcessSTSAndPKP) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hsts-and-hpkp-headers.html"), DEFAULT_PRIORITY, |
| &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| |
| // We should have set parameters from the first header, not the second. |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_TRUE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_TRUE( |
| security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, |
| sts_state.upgrade_mode); |
| #if defined(OS_ANDROID) |
| // Android's CertVerifyProc does not (yet) handle pins. |
| #else |
| EXPECT_TRUE(pkp_state.HasPublicKeyPins()); |
| #endif |
| EXPECT_NE(sts_state.expiry, pkp_state.expiry); |
| |
| // Even though there is an HSTS header asserting includeSubdomains, it is |
| // the *second* such header, and we MUST process only the first. |
| EXPECT_FALSE(sts_state.include_subdomains); |
| // includeSubdomains does not occur in the test HPKP header. |
| EXPECT_FALSE(pkp_state.include_subdomains); |
| } |
| |
| // Tests that when multiple HPKP headers are present, asserting different |
| // policies, that only the first such policy is processed. |
| TEST_F(URLRequestTestHTTP, ProcessSTSAndPKP2) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> request(default_context_.CreateRequest( |
| https_test_server.GetURL("/hsts-and-hpkp-headers2.html"), |
| DEFAULT_PRIORITY, &d)); |
| request->Start(); |
| base::RunLoop().Run(); |
| |
| TransportSecurityState* security_state = |
| default_context_.transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_TRUE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_TRUE( |
| security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, |
| sts_state.upgrade_mode); |
| #if defined(OS_ANDROID) |
| // Android's CertVerifyProc does not (yet) handle pins. |
| #else |
| EXPECT_TRUE(pkp_state.HasPublicKeyPins()); |
| #endif |
| EXPECT_NE(sts_state.expiry, pkp_state.expiry); |
| |
| EXPECT_TRUE(sts_state.include_subdomains); |
| EXPECT_FALSE(pkp_state.include_subdomains); |
| } |
| |
| // An ExpectCTReporter that records the number of times OnExpectCTFailed() was |
| // called. |
| class MockExpectCTReporter : public TransportSecurityState::ExpectCTReporter { |
| public: |
| MockExpectCTReporter() : num_failures_(0) {} |
| ~MockExpectCTReporter() override {} |
| |
| void OnExpectCTFailed(const HostPortPair& host_port_pair, |
| const GURL& report_uri, |
| const net::SSLInfo& ssl_info) override { |
| num_failures_++; |
| } |
| |
| uint32_t num_failures() { return num_failures_; } |
| |
| private: |
| uint32_t num_failures_; |
| }; |
| |
| // A CTVerifier that returns net::OK for every certificate. |
| class MockCTVerifier : public CTVerifier { |
| public: |
| MockCTVerifier() {} |
| ~MockCTVerifier() override {} |
| |
| int Verify(X509Certificate* cert, |
| const std::string& stapled_ocsp_response, |
| const std::string& sct_list_from_tls_extension, |
| ct::CTVerifyResult* result, |
| const BoundNetLog& net_log) override { |
| return net::OK; |
| } |
| |
| void SetObserver(Observer* observer) override {} |
| }; |
| |
| // A CTPolicyEnforcer that returns a default CertPolicyCompliance value |
| // for every certificate. |
| class MockCTPolicyEnforcer : public CTPolicyEnforcer { |
| public: |
| MockCTPolicyEnforcer() |
| : default_result_( |
| ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS) {} |
| ~MockCTPolicyEnforcer() override {} |
| |
| ct::CertPolicyCompliance DoesConformToCertPolicy( |
| X509Certificate* cert, |
| const SCTList& verified_scts, |
| const BoundNetLog& net_log) override { |
| return default_result_; |
| } |
| |
| void set_default_result(ct::CertPolicyCompliance default_result) { |
| default_result_ = default_result; |
| } |
| |
| private: |
| ct::CertPolicyCompliance default_result_; |
| }; |
| |
| // Tests that Expect CT headers are processed correctly. |
| TEST_F(URLRequestTestHTTP, ExpectCTHeader) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| MockExpectCTReporter reporter; |
| TransportSecurityState transport_security_state; |
| transport_security_state.enable_static_expect_ct_ = true; |
| transport_security_state.SetExpectCTReporter(&reporter); |
| |
| // Set up a MockCertVerifier to accept the certificate that the server sends. |
| scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| MockCertVerifier cert_verifier; |
| CertVerifyResult verify_result; |
| verify_result.verified_cert = cert; |
| verify_result.is_issued_by_known_root = true; |
| cert_verifier.AddResultForCert(cert.get(), verify_result, OK); |
| |
| // Set up a MockCTVerifier and MockCTPolicyEnforcer to trigger an Expect CT |
| // violation. |
| MockCTVerifier ct_verifier; |
| MockCTPolicyEnforcer ct_policy_enforcer; |
| ct_policy_enforcer.set_default_result( |
| ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS); |
| |
| TestNetworkDelegate network_delegate; |
| // Use a MockHostResolver (which by default maps all hosts to |
| // 127.0.0.1) so that the request can be sent to a site on the Expect |
| // CT preload list. |
| MockHostResolver host_resolver; |
| TestURLRequestContext context(true); |
| context.set_host_resolver(&host_resolver); |
| context.set_transport_security_state(&transport_security_state); |
| context.set_network_delegate(&network_delegate); |
| context.set_cert_verifier(&cert_verifier); |
| context.set_cert_transparency_verifier(&ct_verifier); |
| context.set_ct_policy_enforcer(&ct_policy_enforcer); |
| context.Init(); |
| |
| // Now send a request to trigger the violation. |
| TestDelegate d; |
| GURL url = https_test_server.GetURL("/expect-ct-header.html"); |
| GURL::Replacements replace_host; |
| replace_host.SetHostStr(kExpectCTStaticHostname); |
| url = url.ReplaceComponents(replace_host); |
| scoped_ptr<URLRequest> violating_request( |
| context.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| violating_request->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1u, reporter.num_failures()); |
| } |
| |
| #endif // !defined(OS_IOS) |
| |
| TEST_F(URLRequestTestHTTP, ContentTypeNormalizationTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/content-type-normalization.html"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| std::string mime_type; |
| req->GetMimeType(&mime_type); |
| EXPECT_EQ("text/html", mime_type); |
| |
| std::string charset; |
| req->GetCharset(&charset); |
| EXPECT_EQ("utf-8", charset); |
| req->Cancel(); |
| } |
| |
| TEST_F(URLRequestTestHTTP, ProtocolHandlerAndFactoryRestrictDataRedirects) { |
| // Test URLRequestJobFactory::ProtocolHandler::IsSafeRedirectTarget(). |
| GURL data_url("data:,foo"); |
| DataProtocolHandler data_protocol_handler; |
| EXPECT_FALSE(data_protocol_handler.IsSafeRedirectTarget(data_url)); |
| |
| // Test URLRequestJobFactoryImpl::IsSafeRedirectTarget(). |
| EXPECT_FALSE(job_factory_->IsSafeRedirectTarget(data_url)); |
| } |
| |
| #if !defined(DISABLE_FILE_SUPPORT) |
| TEST_F(URLRequestTestHTTP, ProtocolHandlerAndFactoryRestrictFileRedirects) { |
| // Test URLRequestJobFactory::ProtocolHandler::IsSafeRedirectTarget(). |
| GURL file_url("file:///foo.txt"); |
| FileProtocolHandler file_protocol_handler( |
| base::ThreadTaskRunnerHandle::Get()); |
| EXPECT_FALSE(file_protocol_handler.IsSafeRedirectTarget(file_url)); |
| |
| // Test URLRequestJobFactoryImpl::IsSafeRedirectTarget(). |
| EXPECT_FALSE(job_factory_->IsSafeRedirectTarget(file_url)); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RestrictFileRedirects) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/redirect-to-file.html"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, req->status().status()); |
| EXPECT_EQ(ERR_UNSAFE_REDIRECT, req->status().error()); |
| } |
| #endif // !defined(DISABLE_FILE_SUPPORT) |
| |
| TEST_F(URLRequestTestHTTP, RestrictDataRedirects) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/redirect-to-data.html"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::MessageLoop::current()->Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, req->status().status()); |
| EXPECT_EQ(ERR_UNSAFE_REDIRECT, req->status().error()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectToInvalidURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/redirect-to-invalid-url.html"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, req->status().status()); |
| EXPECT_EQ(ERR_INVALID_URL, req->status().error()); |
| } |
| |
| // Make sure redirects are cached, despite not reading their bodies. |
| TEST_F(URLRequestTestHTTP, CacheRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL redirect_url = |
| http_test_server()->GetURL("/redirect302-to-echo-cacheable"); |
| |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(redirect_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(http_test_server()->GetURL("/echo"), req->url()); |
| } |
| |
| { |
| TestDelegate d; |
| d.set_quit_on_redirect(true); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(redirect_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(0, d.response_started_count()); |
| EXPECT_TRUE(req->was_cached()); |
| |
| req->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_EQ(http_test_server()->GetURL("/echo"), req->url()); |
| } |
| } |
| |
| // Make sure a request isn't cached when a NetworkDelegate forces a redirect |
| // when the headers are read, since the body won't have been read. |
| TEST_F(URLRequestTestHTTP, NoCacheOnNetworkDelegateRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| // URL that is normally cached. |
| GURL initial_url = http_test_server()->GetURL("/cachetime"); |
| |
| { |
| // Set up the TestNetworkDelegate tp force a redirect. |
| GURL redirect_to_url = http_test_server()->GetURL("/echo"); |
| default_network_delegate_.set_redirect_on_headers_received_url( |
| redirect_to_url); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(initial_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(redirect_to_url, req->url()); |
| } |
| |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(initial_url, DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_FALSE(req->was_cached()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| EXPECT_EQ(initial_url, req->url()); |
| } |
| } |
| |
| // Tests that redirection to an unsafe URL is allowed when it has been marked as |
| // safe. |
| TEST_F(URLRequestTestHTTP, UnsafeRedirectToWhitelistedUnsafeURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL unsafe_url("data:text/html,this-is-considered-an-unsafe-url"); |
| default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url); |
| default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/whatever"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(unsafe_url, r->url()); |
| EXPECT_EQ("this-is-considered-an-unsafe-url", d.data_received()); |
| } |
| } |
| |
| // Tests that a redirect to a different unsafe URL is blocked, even after adding |
| // some other URL to the whitelist. |
| TEST_F(URLRequestTestHTTP, UnsafeRedirectToDifferentUnsafeURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL unsafe_url("data:text/html,something"); |
| GURL different_unsafe_url("data:text/html,something-else"); |
| default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url); |
| default_network_delegate_.set_allowed_unsafe_redirect_url( |
| different_unsafe_url); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/whatever"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| EXPECT_EQ(ERR_UNSAFE_REDIRECT, r->status().error()); |
| } |
| } |
| |
| // Redirects from an URL with fragment to an unsafe URL with fragment should |
| // be allowed, and the reference fragment of the target URL should be preserved. |
| TEST_F(URLRequestTestHTTP, UnsafeRedirectWithDifferentReferenceFragment) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url(http_test_server()->GetURL("/original#fragment1")); |
| GURL unsafe_url("data:,url-marked-safe-and-used-in-redirect#fragment2"); |
| GURL expected_url("data:,url-marked-safe-and-used-in-redirect#fragment2"); |
| |
| default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url); |
| default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(expected_url, r->url()); |
| } |
| } |
| |
| // When a delegate has specified a safe redirect URL, but it does not match the |
| // redirect target, then do not prevent the reference fragment from being added. |
| TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragmentAndUnrelatedUnsafeUrl) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url(http_test_server()->GetURL("/original#expected-fragment")); |
| GURL unsafe_url("data:text/html,this-url-does-not-match-redirect-url"); |
| GURL redirect_url(http_test_server()->GetURL("/target")); |
| GURL expected_redirect_url( |
| http_test_server()->GetURL("/target#expected-fragment")); |
| |
| default_network_delegate_.set_redirect_on_headers_received_url(redirect_url); |
| default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(expected_redirect_url, r->url()); |
| } |
| } |
| |
| // When a delegate has specified a safe redirect URL, assume that the redirect |
| // URL should not be changed. In particular, the reference fragment should not |
| // be modified. |
| TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragment) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url( |
| http_test_server()->GetURL("/original#should-not-be-appended")); |
| GURL redirect_url("data:text/html,expect-no-reference-fragment"); |
| |
| default_network_delegate_.set_redirect_on_headers_received_url(redirect_url); |
| default_network_delegate_.set_allowed_unsafe_redirect_url(redirect_url); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(redirect_url, r->url()); |
| } |
| } |
| |
| // When a URLRequestRedirectJob is created, the redirection must be followed and |
| // the reference fragment of the target URL must not be modified. |
| TEST_F(URLRequestTestHTTP, RedirectJobWithReferenceFragment) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url( |
| http_test_server()->GetURL("/original#should-not-be-appended")); |
| GURL redirect_url(http_test_server()->GetURL("/echo")); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| scoped_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob( |
| r.get(), &default_network_delegate_, redirect_url, |
| URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason")); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(redirect_url, r->url()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, UnsupportedReferrerScheme) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const std::string referrer("foobar://totally.legit.referrer"); |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); |
| req->SetReferrer(referrer); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string("None"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NoUserPassInReferrer) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); |
| req->SetReferrer("http://user:[email protected]/"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string("http://foo.com/"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NoFragmentInReferrer) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); |
| req->SetReferrer("http://foo.com/test#fragment"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string("http://foo.com/test"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, EmptyReferrerAfterValidReferrer) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); |
| req->SetReferrer("http://foo.com/test#fragment"); |
| req->SetReferrer(""); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(std::string("None"), d.data_received()); |
| } |
| |
| // Defer network start and then resume, checking that the request was a success |
| // and bytes were received. |
| TEST_F(URLRequestTestHTTP, DeferredBeforeNetworkStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_quit_on_network_start(true); |
| GURL test_url(http_test_server()->GetURL("/echo")); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_before_network_start_count()); |
| EXPECT_EQ(0, d.response_started_count()); |
| |
| req->ResumeNetworkStart(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| } |
| } |
| |
| // Check that OnBeforeNetworkStart is only called once even if there is a |
| // redirect. |
| TEST_F(URLRequestTestHTTP, BeforeNetworkStartCalledOnce) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_quit_on_redirect(true); |
| d.set_quit_on_network_start(true); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/server-redirect?echo"), DEFAULT_PRIORITY, |
| &d)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_before_network_start_count()); |
| EXPECT_EQ(0, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| |
| req->ResumeNetworkStart(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| req->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| |
| // Check that the redirect's new network transaction does not get propagated |
| // to a second OnBeforeNetworkStart() notification. |
| EXPECT_EQ(1, d.received_before_network_start_count()); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| } |
| } |
| |
| // Cancel the request after learning that the request would use the network. |
| TEST_F(URLRequestTestHTTP, CancelOnBeforeNetworkStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_quit_on_network_start(true); |
| GURL test_url(http_test_server()->GetURL("/echo")); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_before_network_start_count()); |
| EXPECT_EQ(0, d.response_started_count()); |
| |
| req->Cancel(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_cancel_in_received_redirect(true); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, DeferredRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_quit_on_redirect(true); |
| GURL test_url(http_test_server()->GetURL("/redirect-test.html")); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| |
| req->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| |
| base::FilePath path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| path = path.Append(FILE_PATH_LITERAL("with-headers.html")); |
| |
| std::string contents; |
| EXPECT_TRUE(base::ReadFileToString(path, &contents)); |
| EXPECT_EQ(contents, d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, DeferredRedirect_GetFullRequestHeaders) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_quit_on_redirect(true); |
| GURL test_url(http_test_server()->GetURL("/redirect-test.html")); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| |
| EXPECT_FALSE(d.have_full_request_headers()); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_TRUE(d.have_full_request_headers()); |
| CheckFullRequestHeaders(d.full_request_headers(), test_url); |
| d.ClearFullRequestHeaders(); |
| |
| req->FollowDeferredRedirect(); |
| base::RunLoop().Run(); |
| |
| GURL target_url(http_test_server()->GetURL("/with-headers.html")); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_TRUE(d.have_full_request_headers()); |
| CheckFullRequestHeaders(d.full_request_headers(), target_url); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| |
| base::FilePath path; |
| PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| path = path.Append(FILE_PATH_LITERAL("with-headers.html")); |
| |
| std::string contents; |
| EXPECT_TRUE(base::ReadFileToString(path, &contents)); |
| EXPECT_EQ(contents, d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelDeferredRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_quit_on_redirect(true); |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| |
| req->Cancel(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, VaryHeader) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // Populate the cache. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, |
| &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader("foo", "1"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Expect a cache hit. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, |
| &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader("foo", "1"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(req->was_cached()); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingCacheHitNoNetwork(load_timing_info); |
| } |
| |
| // Expect a cache miss. |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, |
| &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader("foo", "2"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(req->was_cached()); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, BasicAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // populate the cache |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| } |
| |
| // repeat request with end-to-end validation. since auth-basic results in a |
| // cachable page, we expect this test to result in a 304. in which case, the |
| // response should be fetched from the cache. |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d)); |
| r->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| // Should be the same cached document. |
| EXPECT_TRUE(r->was_cached()); |
| } |
| } |
| |
| // Check that Set-Cookie headers in 401 responses are respected. |
| // http://crbug.com/6450 |
| TEST_F(URLRequestTestHTTP, BasicAuthWithCookies) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url_requiring_auth = |
| http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); |
| |
| // Request a page that will give a 401 containing a Set-Cookie header. |
| // Verify that when the transaction is restarted, it includes the new cookie. |
| { |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(url_requiring_auth, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| // Make sure we sent the cookie in the restarted transaction. |
| EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") |
| != std::string::npos); |
| } |
| |
| // Same test as above, except this time the restart is initiated earlier |
| // (without user intervention since identity is embedded in the URL). |
| { |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| TestDelegate d; |
| |
| GURL::Replacements replacements; |
| replacements.SetUsernameStr("user2"); |
| replacements.SetPasswordStr("secret"); |
| GURL url_with_identity = url_requiring_auth.ReplaceComponents(replacements); |
| |
| scoped_ptr<URLRequest> r( |
| context.CreateRequest(url_with_identity, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("user2/secret") != std::string::npos); |
| |
| // Make sure we sent the cookie in the restarted transaction. |
| EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") |
| != std::string::npos); |
| } |
| } |
| |
| // Tests that load timing works as expected with auth and the cache. |
| TEST_F(URLRequestTestHTTP, BasicAuthLoadTiming) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // populate the cache |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| LoadTimingInfo load_timing_info_before_auth; |
| EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfoBeforeAuth( |
| &load_timing_info_before_auth)); |
| TestLoadTimingNotReused(load_timing_info_before_auth, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| // The test server does not support keep alive sockets, so the second |
| // request with auth should use a new socket. |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| EXPECT_NE(load_timing_info_before_auth.socket_log_id, |
| load_timing_info.socket_log_id); |
| EXPECT_LE(load_timing_info_before_auth.receive_headers_end, |
| load_timing_info.connect_timing.connect_start); |
| } |
| |
| // Repeat request with end-to-end validation. Since auth-basic results in a |
| // cachable page, we expect this test to result in a 304. In which case, the |
| // response should be fetched from the cache. |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d)); |
| r->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| // Should be the same cached document. |
| EXPECT_TRUE(r->was_cached()); |
| |
| // Since there was a request that went over the wire, the load timing |
| // information should include connection times. |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| } |
| |
| // In this test, we do a POST which the server will 302 redirect. |
| // The subsequent transaction should use GET, and should not send the |
| // Content-Type header. |
| // http://code.google.com/p/chromium/issues/detail?id=843 |
| TEST_F(URLRequestTestHTTP, Post302RedirectGet) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/redirect-to-echoall"), DEFAULT_PRIORITY, |
| &d)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| |
| // Set headers (some of which are specific to the POST). |
| HttpRequestHeaders headers; |
| headers.AddHeadersFromString( |
| "Content-Type: multipart/form-data; " |
| "boundary=----WebKitFormBoundaryAADeAA+NAAWMAAwZ\r\n" |
| "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9," |
| "text/plain;q=0.8,image/png,*/*;q=0.5\r\n" |
| "Accept-Language: en-US,en\r\n" |
| "Accept-Charset: ISO-8859-1,*,utf-8\r\n" |
| "Content-Length: 11\r\n" |
| "Origin: http://localhost:1337/"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| std::string mime_type; |
| req->GetMimeType(&mime_type); |
| EXPECT_EQ("text/html", mime_type); |
| |
| const std::string& data = d.data_received(); |
| |
| // Check that the post-specific headers were stripped: |
| EXPECT_FALSE(ContainsString(data, "Content-Length:")); |
| EXPECT_FALSE(ContainsString(data, "Content-Type:")); |
| EXPECT_FALSE(ContainsString(data, "Origin:")); |
| |
| // These extra request headers should not have been stripped. |
| EXPECT_TRUE(ContainsString(data, "Accept:")); |
| EXPECT_TRUE(ContainsString(data, "Accept-Language:")); |
| EXPECT_TRUE(ContainsString(data, "Accept-Charset:")); |
| } |
| |
| // The following tests check that we handle mutating the request for HTTP |
| // redirects as expected. |
| // See https://crbug.com/56373, https://crbug.com/102130, and |
| // https://crbug.com/465517. |
| |
| TEST_F(URLRequestTestHTTP, Redirect301Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect301-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect301-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "GET", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", |
| std::string()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect302Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect302-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect302-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "GET", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", |
| std::string()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect303Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect303-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect303-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "GET", true); |
| HTTPRedirectMethodTest(url, "PUT", "GET", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", |
| std::string()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect307Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect307-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect307-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "POST", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "POST", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "POST", "null"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect308Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect308-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect308-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "POST", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "POST", url.GetOrigin().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "POST", "null"); |
| } |
| |
| // Make sure that 308 responses without bodies are not treated as redirects. |
| // Certain legacy apis that pre-date the response code expect this behavior |
| // (Like Google Drive). |
| TEST_F(URLRequestTestHTTP, NoRedirectOn308WithoutLocationHeader) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| const GURL url = http_test_server()->GetURL("/308-without-location-header"); |
| |
| scoped_ptr<URLRequest> request( |
| default_context_.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| |
| request->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, request->status().status()); |
| EXPECT_EQ(OK, request->status().error()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| EXPECT_EQ(308, request->response_headers()->response_code()); |
| EXPECT_EQ("This is not a redirect.", d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect302PreserveReferenceFragment) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url( |
| http_test_server()->GetURL("/redirect302-to-echo#fragment")); |
| GURL expected_url(http_test_server()->GetURL("/echo#fragment")); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(original_url, DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(expected_url, r->url()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectPreserveFirstPartyURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url(http_test_server()->GetURL("/redirect302-to-echo")); |
| GURL first_party_url("http://example.com"); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->set_first_party_for_cookies(first_party_url); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(first_party_url, r->first_party_for_cookies()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url(http_test_server()->GetURL("/redirect302-to-echo")); |
| GURL original_first_party_url("http://example.com"); |
| GURL expected_first_party_url(http_test_server()->GetURL("/echo")); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->set_first_party_for_cookies(original_first_party_url); |
| r->set_first_party_url_policy( |
| URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); |
| |
| r->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(URLRequestStatus::SUCCESS, r->status().status()); |
| EXPECT_EQ(OK, r->status().error()); |
| EXPECT_EQ(expected_first_party_url, r->first_party_for_cookies()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::SizeTToString(arraysize(kData) - 1)); |
| req->SetExtraRequestHeaders(headers); |
| |
| scoped_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob( |
| req.get(), &default_network_delegate_, |
| http_test_server()->GetURL("/echo"), |
| URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason")); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ("GET", req->method()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, InterceptPost307RedirectPost) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::SizeTToString(arraysize(kData) - 1)); |
| req->SetExtraRequestHeaders(headers); |
| |
| scoped_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob( |
| req.get(), &default_network_delegate_, |
| http_test_server()->GetURL("/echo"), |
| URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, |
| "Very Good Reason")); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ("POST", req->method()); |
| EXPECT_EQ(kData, d.data_received()); |
| } |
| |
| // Check that default A-L header is sent. |
| TEST_F(URLRequestTestHTTP, DefaultAcceptLanguage) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| StaticHttpUserAgentSettings settings("en", std::string()); |
| TestNetworkDelegate network_delegate; // Must outlive URLRequests. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_http_user_agent_settings(&settings); |
| context.Init(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Language"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ("en", d.data_received()); |
| } |
| |
| // Check that an empty A-L header is not sent. http://crbug.com/77365. |
| TEST_F(URLRequestTestHTTP, EmptyAcceptLanguage) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| std::string empty_string; // Avoid most vexing parse on line below. |
| StaticHttpUserAgentSettings settings(empty_string, empty_string); |
| TestNetworkDelegate network_delegate; // Must outlive URLRequests. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| // We override the language after initialization because empty entries |
| // get overridden by Init(). |
| context.set_http_user_agent_settings(&settings); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Language"), |
| DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ("None", d.data_received()); |
| } |
| |
| // Check that if request overrides the A-L header, the default is not appended. |
| // See http://crbug.com/20894 |
| TEST_F(URLRequestTestHTTP, OverrideAcceptLanguage) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Language"), |
| DEFAULT_PRIORITY, &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kAcceptLanguage, "ru"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(std::string("ru"), d.data_received()); |
| } |
| |
| // Check that default A-E header is sent. |
| TEST_F(URLRequestTestHTTP, DefaultAcceptEncoding) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Encoding"), |
| DEFAULT_PRIORITY, &d)); |
| HttpRequestHeaders headers; |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(ContainsString(d.data_received(), "gzip")); |
| } |
| |
| // Check that if request overrides the A-E header, the default is not appended. |
| // See http://crbug.com/47381 |
| TEST_F(URLRequestTestHTTP, OverrideAcceptEncoding) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Encoding"), |
| DEFAULT_PRIORITY, &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kAcceptEncoding, "identity"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_FALSE(ContainsString(d.data_received(), "gzip")); |
| EXPECT_TRUE(ContainsString(d.data_received(), "identity")); |
| } |
| |
| // Check that setting the A-C header sends the proper header. |
| TEST_F(URLRequestTestHTTP, SetAcceptCharset) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Charset"), |
| DEFAULT_PRIORITY, &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kAcceptCharset, "koi-8r"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(std::string("koi-8r"), d.data_received()); |
| } |
| |
| // Check that default User-Agent header is sent. |
| TEST_F(URLRequestTestHTTP, DefaultUserAgent) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?User-Agent"), DEFAULT_PRIORITY, |
| &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(default_context_.http_user_agent_settings()->GetUserAgent(), |
| d.data_received()); |
| } |
| |
| // Check that if request overrides the User-Agent header, |
| // the default is not appended. |
| // TODO(crbug.com/564656) This test is flaky on iOS. |
| #if defined(OS_IOS) |
| #define MAYBE_OverrideUserAgent FLAKY_OverrideUserAgent |
| #else |
| #define MAYBE_OverrideUserAgent OverrideUserAgent |
| #endif |
| TEST_F(URLRequestTestHTTP, MAYBE_OverrideUserAgent) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/echoheader?User-Agent"), DEFAULT_PRIORITY, |
| &d)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kUserAgent, "Lynx (textmode)"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(std::string("Lynx (textmode)"), d.data_received()); |
| } |
| |
| // Check that a NULL HttpUserAgentSettings causes the corresponding empty |
| // User-Agent header to be sent but does not send the Accept-Language and |
| // Accept-Charset headers. |
| TEST_F(URLRequestTestHTTP, EmptyHttpUserAgentSettings) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequests. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| // We override the HttpUserAgentSettings after initialization because empty |
| // entries get overridden by Init(). |
| context.set_http_user_agent_settings(NULL); |
| |
| struct { |
| const char* request; |
| const char* expected_response; |
| } tests[] = {{"/echoheader?Accept-Language", "None"}, |
| {"/echoheader?Accept-Charset", "None"}, |
| {"/echoheader?User-Agent", ""}}; |
| |
| for (size_t i = 0; i < arraysize(tests); i++) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| http_test_server()->GetURL(tests[i].request), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_EQ(tests[i].expected_response, d.data_received()) |
| << " Request = \"" << tests[i].request << "\""; |
| } |
| } |
| |
| // Make sure that URLRequest passes on its priority updates to |
| // newly-created jobs after the first one. |
| TEST_F(URLRequestTestHTTP, SetSubsequentJobPriority) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); |
| |
| scoped_ptr<URLRequestRedirectJob> redirect_job(new URLRequestRedirectJob( |
| req.get(), &default_network_delegate_, |
| http_test_server()->GetURL("/echo"), |
| URLRequestRedirectJob::REDIRECT_302_FOUND, "Very Good Reason")); |
| AddTestInterceptor()->set_main_intercept_job(std::move(redirect_job)); |
| |
| req->SetPriority(LOW); |
| req->Start(); |
| EXPECT_TRUE(req->is_pending()); |
| |
| RequestPriority job_priority; |
| scoped_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| // Should trigger |job| to be started. |
| base::RunLoop().Run(); |
| EXPECT_EQ(LOW, job_priority); |
| } |
| |
| // Check that creating a network request while entering/exiting suspend mode |
| // fails as it should. This is the only case where an HttpTransactionFactory |
| // does not return an HttpTransaction. |
| TEST_F(URLRequestTestHTTP, NetworkSuspendTest) { |
| // Create a new HttpNetworkLayer that thinks it's suspended. |
| scoped_ptr<HttpNetworkLayer> network_layer(new HttpNetworkLayer( |
| default_context_.http_transaction_factory()->GetSession())); |
| network_layer->OnSuspend(); |
| |
| HttpCache http_cache(std::move(network_layer), |
| HttpCache::DefaultBackend::InMemory(0), true); |
| |
| TestURLRequestContext context(true); |
| context.set_http_transaction_factory(&http_cache); |
| context.Init(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://127.0.0.1/"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_EQ(URLRequestStatus::FAILED, req->status().status()); |
| EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, req->status().error()); |
| } |
| |
| namespace { |
| |
| // HttpTransactionFactory that synchronously fails to create transactions. |
| class FailingHttpTransactionFactory : public HttpTransactionFactory { |
| public: |
| explicit FailingHttpTransactionFactory(HttpNetworkSession* network_session) |
| : network_session_(network_session) {} |
| |
| ~FailingHttpTransactionFactory() override {} |
| |
| // HttpTransactionFactory methods: |
| int CreateTransaction(RequestPriority priority, |
| scoped_ptr<HttpTransaction>* trans) override { |
| return ERR_FAILED; |
| } |
| |
| HttpCache* GetCache() override { return nullptr; } |
| |
| HttpNetworkSession* GetSession() override { return network_session_; } |
| |
| private: |
| HttpNetworkSession* network_session_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FailingHttpTransactionFactory); |
| }; |
| |
| } // namespace |
| |
| // Check that when a request that fails to create an HttpTransaction can be |
| // cancelled while the failure notification is pending, and doesn't send two |
| // failure notifications. |
| // |
| // This currently only happens when in suspend mode and there's no cache, but |
| // just use a special HttpTransactionFactory, to avoid depending on those |
| // behaviors. |
| TEST_F(URLRequestTestHTTP, NetworkCancelAfterCreateTransactionFailsTest) { |
| FailingHttpTransactionFactory http_transaction_factory( |
| default_context_.http_transaction_factory()->GetSession()); |
| TestURLRequestContext context(true); |
| context.set_http_transaction_factory(&http_transaction_factory); |
| context.set_network_delegate(default_network_delegate()); |
| context.Init(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://127.0.0.1/"), DEFAULT_PRIORITY, &d)); |
| // Don't send cookies (Collecting cookies is asynchronous, and need request to |
| // try to create an HttpNetworkTransaction synchronously on start). |
| req->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES); |
| req->Start(); |
| req->Cancel(); |
| base::RunLoop().Run(); |
| // Run pending error task, if there is one. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| |
| // NetworkDelegate should see the cancellation, but not the error. |
| EXPECT_EQ(1, default_network_delegate()->canceled_requests()); |
| EXPECT_EQ(0, default_network_delegate()->error_count()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkAccessedSetOnNetworkRequest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| GURL test_url(http_test_server()->GetURL("/")); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(req->response_info().network_accessed); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkAccessedClearOnCachedResponse) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // Populate the cache. |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_TRUE(req->response_info().network_accessed); |
| EXPECT_FALSE(req->response_info().was_cached); |
| |
| req = default_context_.CreateRequest(http_test_server()->GetURL("/cachetime"), |
| DEFAULT_PRIORITY, &d); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(URLRequestStatus::SUCCESS, req->status().status()); |
| EXPECT_FALSE(req->response_info().network_accessed); |
| EXPECT_TRUE(req->response_info().was_cached); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkAccessedClearOnLoadOnlyFromCache) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| GURL test_url(http_test_server()->GetURL("/")); |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(test_url, DEFAULT_PRIORITY, &d)); |
| req->SetLoadFlags(LOAD_ONLY_FROM_CACHE); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(req->response_info().network_accessed); |
| } |
| |
| class URLRequestInterceptorTestHTTP : public URLRequestTestHTTP { |
| public: |
| // TODO(bengr): Merge this with the URLRequestInterceptorHTTPTest fixture, |
| // ideally remove the dependency on URLRequestTestJob, and maybe move these |
| // tests into the factory tests. |
| URLRequestInterceptorTestHTTP() : URLRequestTestHTTP(), interceptor_(NULL) { |
| } |
| |
| void SetUpFactory() override { |
| interceptor_ = new MockURLRequestInterceptor(); |
| job_factory_.reset(new URLRequestInterceptingJobFactory( |
| std::move(job_factory_), make_scoped_ptr(interceptor_))); |
| } |
| |
| MockURLRequestInterceptor* interceptor() const { |
| return interceptor_; |
| } |
| |
| private: |
| MockURLRequestInterceptor* interceptor_; |
| }; |
| |
| TEST_F(URLRequestInterceptorTestHTTP, |
| NetworkDelegateNotificationOnRedirectIntercept) { |
| interceptor()->set_intercept_redirect(true); |
| interceptor()->set_redirect_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_redirect_data(MockURLRequestInterceptor::ok_data()); |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, &d)); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(interceptor()->did_intercept_redirect()); |
| // Check we got one good response |
| EXPECT_TRUE(req->status().is_success()); |
| if (req->status().is_success()) |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| |
| EXPECT_EQ(1, default_network_delegate()->created_requests()); |
| EXPECT_EQ(1, default_network_delegate()->before_send_headers_count()); |
| EXPECT_EQ(1, default_network_delegate()->headers_received_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTestHTTP, |
| NetworkDelegateNotificationOnErrorIntercept) { |
| // Intercept that error and respond with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); |
| interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); |
| default_network_delegate()->set_can_be_intercepted_on_error(true); |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/two-content-lengths.html"), DEFAULT_PRIORITY, |
| &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(interceptor()->did_intercept_final()); |
| |
| // Check we received one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| if (req->status().is_success()) |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| |
| EXPECT_EQ(1, default_network_delegate()->created_requests()); |
| EXPECT_EQ(1, default_network_delegate()->before_send_headers_count()); |
| EXPECT_EQ(0, default_network_delegate()->headers_received_count()); |
| } |
| |
| TEST_F(URLRequestInterceptorTestHTTP, |
| NetworkDelegateNotificationOnResponseIntercept) { |
| // Intercept that error and respond with an OK response. |
| interceptor()->set_intercept_final_response(true); |
| |
| // Intercept with a real URLRequestHttpJob. |
| interceptor()->set_use_url_request_http_job(true); |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/simple.html"), DEFAULT_PRIORITY, &d)); |
| req->set_method("GET"); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(interceptor()->did_intercept_final()); |
| |
| // Check we received one good response. |
| EXPECT_TRUE(req->status().is_success()); |
| if (req->status().is_success()) |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| EXPECT_EQ("hello", d.data_received()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| |
| EXPECT_EQ(1, default_network_delegate()->created_requests()); |
| EXPECT_EQ(2, default_network_delegate()->before_send_headers_count()); |
| EXPECT_EQ(2, default_network_delegate()->headers_received_count()); |
| } |
| |
| class URLRequestTestReferrerPolicy : public URLRequestTest { |
| public: |
| URLRequestTestReferrerPolicy() {} |
| |
| void InstantiateSameOriginServers(net::EmbeddedTestServer::Type type) { |
| origin_server_.reset(new EmbeddedTestServer(type)); |
| if (type == net::EmbeddedTestServer::TYPE_HTTPS) { |
| origin_server_->AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| } else { |
| origin_server_->AddDefaultHandlers(base::FilePath(kTestFilePath)); |
| } |
| ASSERT_TRUE(origin_server_->Start()); |
| } |
| |
| void InstantiateCrossOriginServers(net::EmbeddedTestServer::Type origin_type, |
| net::EmbeddedTestServer::Type dest_type) { |
| origin_server_.reset(new EmbeddedTestServer(origin_type)); |
| if (origin_type == net::EmbeddedTestServer::TYPE_HTTPS) { |
| origin_server_->AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| } else { |
| origin_server_->AddDefaultHandlers(base::FilePath(kTestFilePath)); |
| } |
| ASSERT_TRUE(origin_server_->Start()); |
| |
| destination_server_.reset(new EmbeddedTestServer(dest_type)); |
| if (dest_type == net::EmbeddedTestServer::TYPE_HTTPS) { |
| destination_server_->AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| } else { |
| destination_server_->AddDefaultHandlers(base::FilePath(kTestFilePath)); |
| } |
| ASSERT_TRUE(destination_server_->Start()); |
| } |
| |
| void VerifyReferrerAfterRedirect(URLRequest::ReferrerPolicy policy, |
| const GURL& referrer, |
| const GURL& expected) { |
| // Create and execute the request: we'll only have a |destination_server_| |
| // if the origins are meant to be distinct. Otherwise, we'll use the |
| // |origin_server_| for both endpoints. |
| GURL destination_url = |
| destination_server_ ? destination_server_->GetURL("/echoheader?Referer") |
| : origin_server_->GetURL("/echoheader?Referer"); |
| GURL origin_url = |
| origin_server_->GetURL("/server-redirect?" + destination_url.spec()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(origin_url, DEFAULT_PRIORITY, &d)); |
| req->set_referrer_policy(policy); |
| req->SetReferrer(referrer.spec()); |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(destination_url, req->url()); |
| EXPECT_TRUE(req->status().is_success()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| EXPECT_EQ(expected.spec(), req->referrer()); |
| if (expected.is_empty()) |
| EXPECT_EQ("None", d.data_received()); |
| else |
| EXPECT_EQ(expected.spec(), d.data_received()); |
| } |
| |
| EmbeddedTestServer* origin_server() const { return origin_server_.get(); } |
| |
| private: |
| scoped_ptr<EmbeddedTestServer> origin_server_; |
| scoped_ptr<EmbeddedTestServer> destination_server_; |
| }; |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPToSameOriginHTTP) { |
| InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTP); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPToCrossOriginHTTP) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP, |
| net::EmbeddedTestServer::TYPE_HTTP); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPSToSameOriginHTTPS) { |
| InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTPS); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPSToCrossOriginHTTPS) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS, |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPToHTTPS) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP, |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPSToHTTP) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS, |
| net::EmbeddedTestServer::TYPE_HTTP); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| origin_server()->GetURL("/path/to/file.html"), GURL()); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), GURL()); |
| |
| VerifyReferrerAfterRedirect( |
| URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(URLRequest::NEVER_CLEAR_REFERRER, |
| origin_server()->GetURL("/path/to/file.html"), |
| origin_server()->GetURL("/path/to/file.html")); |
| } |
| |
| class HTTPSRequestTest : public testing::Test { |
| public: |
| HTTPSRequestTest() : default_context_(true) { |
| default_context_.set_network_delegate(&default_network_delegate_); |
| default_context_.Init(); |
| } |
| ~HTTPSRequestTest() override {} |
| |
| protected: |
| TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. |
| TestURLRequestContext default_context_; |
| }; |
| |
| TEST_F(HTTPSRequestTest, HTTPSGetTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| CheckSSLInfo(r->ssl_info()); |
| EXPECT_EQ(test_server.host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(test_server.host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, HTTPSMismatchedTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| test_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| bool err_allowed = true; |
| for (int i = 0; i < 2 ; i++, err_allowed = !err_allowed) { |
| TestDelegate d; |
| { |
| d.set_allow_certificate_errors(err_allowed); |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| if (err_allowed) { |
| EXPECT_NE(0, d.bytes_received()); |
| CheckSSLInfo(r->ssl_info()); |
| } else { |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| } |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, HTTPSExpiredTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| test_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Iterate from false to true, just so that we do the opposite of the |
| // previous test in order to increase test coverage. |
| bool err_allowed = false; |
| for (int i = 0; i < 2 ; i++, err_allowed = !err_allowed) { |
| TestDelegate d; |
| { |
| d.set_allow_certificate_errors(err_allowed); |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| if (err_allowed) { |
| EXPECT_NE(0, d.bytes_received()); |
| CheckSSLInfo(r->ssl_info()); |
| } else { |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| } |
| } |
| } |
| |
| // TODO(svaldez): iOS tests are flaky with EmbeddedTestServer and transport |
| // security state. (see http://crbug.com/550977). |
| #if !defined(OS_IOS) |
| // This tests that a load of www.google.com with a certificate error sets |
| // the |certificate_errors_are_fatal| flag correctly. This flag will cause |
| // the interstitial to be fatal. |
| TEST_F(HTTPSRequestTest, HTTPSPreloadedHSTSTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // We require that the URL be www.google.com in order to pick up the |
| // preloaded HSTS entries in the TransportSecurityState. This means that we |
| // have to use a MockHostResolver in order to direct www.google.com to the |
| // testserver. By default, MockHostResolver maps all hosts to 127.0.0.1. |
| |
| MockHostResolver host_resolver; |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_host_resolver(&host_resolver); |
| TransportSecurityState transport_security_state; |
| context.set_transport_security_state(&transport_security_state); |
| context.Init(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| GURL(base::StringPrintf("https://www.google.com:%d", |
| test_server.host_port_pair().port())), |
| DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_TRUE(d.certificate_errors_are_fatal()); |
| } |
| |
| // This tests that cached HTTPS page loads do not cause any updates to the |
| // TransportSecurityState. |
| TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) { |
| // The actual problem -- CERT_MISMATCHED_NAME in this case -- doesn't |
| // matter. It just has to be any error. |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // We require that the URL be www.google.com in order to pick up the static |
| // and dynamic STS and PKP entries in the TransportSecurityState. This means |
| // that we have to use a MockHostResolver in order to direct www.google.com to |
| // the testserver. By default, MockHostResolver maps all hosts to 127.0.0.1. |
| |
| MockHostResolver host_resolver; |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_host_resolver(&host_resolver); |
| TransportSecurityState transport_security_state; |
| |
| TransportSecurityState::STSState static_sts_state; |
| TransportSecurityState::PKPState static_pkp_state; |
| EXPECT_TRUE(transport_security_state.GetStaticDomainState( |
| "www.google.com", &static_sts_state, &static_pkp_state)); |
| context.set_transport_security_state(&transport_security_state); |
| context.Init(); |
| |
| TransportSecurityState::STSState dynamic_sts_state; |
| TransportSecurityState::PKPState dynamic_pkp_state; |
| EXPECT_FALSE(transport_security_state.GetDynamicSTSState("www.google.com", |
| &dynamic_sts_state)); |
| EXPECT_FALSE(transport_security_state.GetDynamicPKPState("www.google.com", |
| &dynamic_pkp_state)); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(context.CreateRequest( |
| GURL(base::StringPrintf("https://www.google.com:%d", |
| test_server.host_port_pair().port())), |
| DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_TRUE(d.certificate_errors_are_fatal()); |
| |
| // Get a fresh copy of the states, and check that they haven't changed. |
| TransportSecurityState::STSState new_static_sts_state; |
| TransportSecurityState::PKPState new_static_pkp_state; |
| EXPECT_TRUE(transport_security_state.GetStaticDomainState( |
| "www.google.com", &new_static_sts_state, &new_static_pkp_state)); |
| TransportSecurityState::STSState new_dynamic_sts_state; |
| TransportSecurityState::PKPState new_dynamic_pkp_state; |
| EXPECT_FALSE(transport_security_state.GetDynamicSTSState( |
| "www.google.com", &new_dynamic_sts_state)); |
| EXPECT_FALSE(transport_security_state.GetDynamicPKPState( |
| "www.google.com", &new_dynamic_pkp_state)); |
| |
| EXPECT_EQ(new_static_sts_state.upgrade_mode, static_sts_state.upgrade_mode); |
| EXPECT_EQ(new_static_sts_state.include_subdomains, |
| static_sts_state.include_subdomains); |
| EXPECT_EQ(new_static_pkp_state.include_subdomains, |
| static_pkp_state.include_subdomains); |
| EXPECT_TRUE(FingerprintsEqual(new_static_pkp_state.spki_hashes, |
| static_pkp_state.spki_hashes)); |
| EXPECT_TRUE(FingerprintsEqual(new_static_pkp_state.bad_spki_hashes, |
| static_pkp_state.bad_spki_hashes)); |
| } |
| |
| // Make sure HSTS preserves a POST request's method and body. |
| TEST_F(HTTPSRequestTest, HSTSPreservesPosts) { |
| static const char kData[] = "hello world"; |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| |
| // Per spec, TransportSecurityState expects a domain name, rather than an IP |
| // address, so a MockHostResolver is needed to redirect www.somewhere.com to |
| // the EmbeddedTestServer. By default, MockHostResolver maps all hosts |
| // to 127.0.0.1. |
| MockHostResolver host_resolver; |
| |
| // Force https for www.somewhere.com. |
| TransportSecurityState transport_security_state; |
| base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000); |
| bool include_subdomains = false; |
| transport_security_state.AddHSTS("www.somewhere.com", expiry, |
| include_subdomains); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| |
| TestURLRequestContext context(true); |
| context.set_host_resolver(&host_resolver); |
| context.set_transport_security_state(&transport_security_state); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| TestDelegate d; |
| // Navigating to https://www.somewhere.com instead of https://127.0.0.1 will |
| // cause a certificate error. Ignore the error. |
| d.set_allow_certificate_errors(true); |
| |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| GURL(base::StringPrintf("http://www.somewhere.com:%d/echo", |
| test_server.host_port_pair().port())), |
| DEFAULT_PRIORITY, &d)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ("https", req->url().scheme()); |
| EXPECT_EQ("POST", req->method()); |
| EXPECT_EQ(kData, d.data_received()); |
| |
| LoadTimingInfo load_timing_info; |
| network_delegate.GetLoadTimingInfoBeforeRedirect(&load_timing_info); |
| // LoadTimingInfo of HSTS redirects is similar to that of network cache hits |
| TestLoadTimingCacheHitNoNetwork(load_timing_info); |
| } |
| |
| // Make sure that the CORS headers are added to cross-origin HSTS redirects. |
| TEST_F(HTTPSRequestTest, HSTSCrossOriginAddHeaders) { |
| static const char kOriginHeaderValue[] = "http://www.example.com"; |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Per spec, TransportSecurityState expects a domain name, rather than an IP |
| // address, so a MockHostResolver is needed to redirect example.net to the |
| // EmbeddedTestServer. MockHostResolver maps all hosts to 127.0.0.1 by |
| // default. |
| MockHostResolver host_resolver; |
| |
| TransportSecurityState transport_security_state; |
| base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1); |
| bool include_subdomains = false; |
| transport_security_state.AddHSTS("example.net", expiry, include_subdomains); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| |
| MockCertVerifier cert_verifier; |
| cert_verifier.set_default_result(OK); |
| |
| TestURLRequestContext context(true); |
| context.set_host_resolver(&host_resolver); |
| context.set_transport_security_state(&transport_security_state); |
| context.set_network_delegate(&network_delegate); |
| context.set_cert_verifier(&cert_verifier); |
| context.Init(); |
| |
| GURL hsts_http_url(base::StringPrintf("http://example.net:%d/somehstssite", |
| test_server.host_port_pair().port())); |
| url::Replacements<char> replacements; |
| const char kNewScheme[] = "https"; |
| replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme))); |
| GURL hsts_https_url = hsts_http_url.ReplaceComponents(replacements); |
| |
| TestDelegate d; |
| // Quit on redirect to allow response header inspection upon redirect. |
| d.set_quit_on_redirect(true); |
| |
| scoped_ptr<URLRequest> req( |
| context.CreateRequest(hsts_http_url, DEFAULT_PRIORITY, &d)); |
| // Set Origin header to simulate a cross-origin request. |
| HttpRequestHeaders request_headers; |
| request_headers.SetHeader("Origin", kOriginHeaderValue); |
| req->SetExtraRequestHeaders(request_headers); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| |
| const HttpResponseHeaders* headers = req->response_headers(); |
| std::string redirect_location; |
| EXPECT_TRUE(headers->EnumerateHeader(NULL, "Location", &redirect_location)); |
| EXPECT_EQ(hsts_https_url.spec(), redirect_location); |
| |
| std::string received_cors_header; |
| EXPECT_TRUE(headers->EnumerateHeader(NULL, "Access-Control-Allow-Origin", |
| &received_cors_header)); |
| EXPECT_EQ(kOriginHeaderValue, received_cors_header); |
| } |
| |
| // This just tests the behaviour of GetHSTSRedirect(). End-to-end tests of HSTS |
| // are performed in net/websockets/websocket_end_to_end_test.cc. |
| TEST(WebSocketURLRequestTest, HSTSApplied) { |
| TestNetworkDelegate network_delegate; |
| TransportSecurityState transport_security_state; |
| base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1); |
| bool include_subdomains = false; |
| transport_security_state.AddHSTS("example.net", expiry, include_subdomains); |
| TestURLRequestContext context(true); |
| context.set_transport_security_state(&transport_security_state); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| GURL ws_url("ws://example.net/echo"); |
| TestDelegate delegate; |
| scoped_ptr<URLRequest> request( |
| context.CreateRequest(ws_url, DEFAULT_PRIORITY, &delegate)); |
| EXPECT_TRUE(request->GetHSTSRedirect(&ws_url)); |
| EXPECT_TRUE(ws_url.SchemeIs("wss")); |
| } |
| |
| namespace { |
| |
| class SSLClientAuthTestDelegate : public TestDelegate { |
| public: |
| SSLClientAuthTestDelegate() : on_certificate_requested_count_(0) { |
| } |
| void OnCertificateRequested(URLRequest* request, |
| SSLCertRequestInfo* cert_request_info) override { |
| on_certificate_requested_count_++; |
| base::MessageLoop::current()->QuitWhenIdle(); |
| } |
| int on_certificate_requested_count() { |
| return on_certificate_requested_count_; |
| } |
| private: |
| int on_certificate_requested_count_; |
| }; |
| |
| } // namespace |
| |
| // TODO(davidben): Test the rest of the code. Specifically, |
| // - Filtering which certificates to select. |
| // - Sending a certificate back. |
| // - Getting a certificate request in an SSL renegotiation sending the |
| // HTTP request. |
| TEST_F(HTTPSRequestTest, ClientAuthTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_cert_type = |
| SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT; |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); |
| test_server.AddDefaultHandlers( |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLClientAuthTestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| // Send no certificate. |
| // TODO(davidben): Get temporary client cert import (with keys) working on |
| // all platforms so we can test sending a cert as well. |
| r->ContinueWithCertificate(nullptr, nullptr); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, ResumeTest) { |
| // Test that we attempt a session resume when making two connections to the |
| // same host. |
| SpawnedTestServer::SSLOptions ssl_options; |
| ssl_options.record_resume = true; |
| SpawnedTestServer test_server( |
| SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLClientSocket::ClearSessionCache(); |
| |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| } |
| |
| reinterpret_cast<HttpCache*>(default_context_.http_transaction_factory())-> |
| CloseAllConnections(); |
| |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| // The response will look like; |
| // insert abc |
| // lookup abc |
| // insert xyz |
| // |
| // With a newline at the end which makes the split think that there are |
| // four lines. |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| std::vector<std::string> lines = base::SplitString( |
| d.data_received(), "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| ASSERT_EQ(4u, lines.size()) << d.data_received(); |
| |
| std::string session_id; |
| |
| for (size_t i = 0; i < 2; i++) { |
| std::vector<std::string> parts = base::SplitString( |
| lines[i], "\t", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| ASSERT_EQ(2u, parts.size()); |
| if (i == 0) { |
| EXPECT_EQ("insert", parts[0]); |
| session_id = parts[1]; |
| } else { |
| EXPECT_EQ("lookup", parts[0]); |
| EXPECT_EQ(session_id, parts[1]); |
| } |
| } |
| } |
| } |
| |
| // AssertTwoDistinctSessionsInserted checks that |session_info|, which must be |
| // the result of fetching "ssl-session-cache" from the test server, indicates |
| // that exactly two different sessions were inserted, with no lookups etc. |
| static void AssertTwoDistinctSessionsInserted(const string& session_info) { |
| std::vector<std::string> lines = base::SplitString( |
| session_info, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| ASSERT_EQ(3u, lines.size()) << session_info; |
| |
| std::string session_id; |
| for (size_t i = 0; i < 2; i++) { |
| std::vector<std::string> parts = base::SplitString( |
| lines[i], "\t", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| ASSERT_EQ(2u, parts.size()); |
| EXPECT_EQ("insert", parts[0]); |
| if (i == 0) { |
| session_id = parts[1]; |
| } else { |
| EXPECT_NE(session_id, parts[1]); |
| } |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, SSLSessionCacheShardTest) { |
| // Test that sessions aren't resumed when the value of ssl_session_cache_shard |
| // differs. |
| SpawnedTestServer::SSLOptions ssl_options; |
| ssl_options.record_resume = true; |
| SpawnedTestServer test_server( |
| SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLClientSocket::ClearSessionCache(); |
| |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| } |
| |
| // Now create a new HttpCache with a different ssl_session_cache_shard value. |
| HttpNetworkSession::Params params; |
| params.host_resolver = default_context_.host_resolver(); |
| params.cert_verifier = default_context_.cert_verifier(); |
| params.transport_security_state = default_context_.transport_security_state(); |
| params.proxy_service = default_context_.proxy_service(); |
| params.ssl_config_service = default_context_.ssl_config_service(); |
| params.http_auth_handler_factory = |
| default_context_.http_auth_handler_factory(); |
| params.http_server_properties = default_context_.http_server_properties(); |
| |
| HttpNetworkSession network_session(params); |
| scoped_ptr<HttpCache> cache(new HttpCache( |
| &network_session, HttpCache::DefaultBackend::InMemory(0), false)); |
| |
| default_context_.set_http_transaction_factory(cache.get()); |
| |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| // The response will look like; |
| // insert abc |
| // insert xyz |
| // |
| // With a newline at the end which makes the split think that there are |
| // three lines. |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| AssertTwoDistinctSessionsInserted(d.data_received()); |
| } |
| } |
| |
| #if defined(OS_WIN) |
| |
| namespace { |
| |
| bool IsECDSACipherSuite(uint16_t cipher_suite) { |
| const char* key_exchange; |
| const char* cipher; |
| const char* mac; |
| bool is_aead; |
| SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead, cipher_suite); |
| return std::string(key_exchange).find("ECDSA") != std::string::npos; |
| } |
| |
| } // namespace |
| |
| // Test that ECDSA is disabled on Windows XP, where ECDSA certificates cannot be |
| // verified. |
| TEST_F(HTTPSRequestTest, DisableECDSAOnXP) { |
| if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
| LOG(INFO) << "Skipping test on this version."; |
| return; |
| } |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("/client-cipher-list"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| std::vector<std::string> lines = base::SplitString( |
| d.data_received(), "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| |
| for (size_t i = 0; i < lines.size(); i++) { |
| int cipher_suite; |
| ASSERT_TRUE(base::StringToInt(lines[i], &cipher_suite)); |
| EXPECT_FALSE(IsECDSACipherSuite(cipher_suite)) |
| << "ClientHello advertised " << cipher_suite; |
| } |
| } |
| |
| #endif // OS_WIN |
| |
| class FallbackTestURLRequestContext : public TestURLRequestContext { |
| public: |
| explicit FallbackTestURLRequestContext(bool delay_initialization) |
| : TestURLRequestContext(delay_initialization) {} |
| |
| void set_fallback_min_version(uint16_t version) { |
| TestSSLConfigService* ssl_config_service = new TestSSLConfigService( |
| true /* check for EV */, false /* online revocation checking */, |
| false /* require rev. checking for local |
| anchors */, |
| false /* token binding enabled */); |
| ssl_config_service->set_fallback_min_version(version); |
| set_ssl_config_service(ssl_config_service); |
| } |
| }; |
| |
| class HTTPSFallbackTest : public testing::Test { |
| public: |
| HTTPSFallbackTest() : context_(true) {} |
| ~HTTPSFallbackTest() override {} |
| |
| protected: |
| void DoFallbackTest(const SpawnedTestServer::SSLOptions& ssl_options) { |
| DCHECK(!request_); |
| context_.Init(); |
| delegate_.set_allow_certificate_errors(true); |
| |
| SpawnedTestServer test_server( |
| SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| request_ = context_.CreateRequest(test_server.GetURL("/"), DEFAULT_PRIORITY, |
| &delegate_); |
| request_->Start(); |
| |
| base::RunLoop().Run(); |
| } |
| |
| void set_fallback_min_version(uint16_t version) { |
| context_.set_fallback_min_version(version); |
| } |
| |
| void ExpectConnection(int version) { |
| EXPECT_EQ(1, delegate_.response_started_count()); |
| EXPECT_NE(0, delegate_.bytes_received()); |
| EXPECT_EQ(version, SSLConnectionStatusToVersion( |
| request_->ssl_info().connection_status)); |
| EXPECT_TRUE(request_->ssl_info().connection_status & |
| SSL_CONNECTION_VERSION_FALLBACK); |
| } |
| |
| void ExpectFailure(int error) { |
| EXPECT_EQ(1, delegate_.response_started_count()); |
| EXPECT_FALSE(request_->status().is_success()); |
| EXPECT_EQ(URLRequestStatus::FAILED, request_->status().status()); |
| EXPECT_EQ(error, request_->status().error()); |
| } |
| |
| private: |
| TestDelegate delegate_; |
| FallbackTestURLRequestContext context_; |
| scoped_ptr<URLRequest> request_; |
| }; |
| |
| // Tests the TLS 1.0 fallback doesn't happen. |
| TEST_F(HTTPSFallbackTest, TLSv1NoFallback) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_1; |
| |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); |
| } |
| |
| // Tests the TLS 1.1 fallback doesn't happen but 1.2-intolerance is detected. |
| TEST_F(HTTPSFallbackTest, TLSv1_1NoFallback) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_2; |
| |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| ExpectFailure(ERR_SSL_FALLBACK_BEYOND_MINIMUM_VERSION); |
| } |
| |
| // Tests the TLS 1.1 fallback when explicitly enabled. |
| TEST_F(HTTPSFallbackTest, TLSv1_1Fallback) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_2; |
| |
| set_fallback_min_version(SSL_PROTOCOL_VERSION_TLS1_1); |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| ExpectConnection(SSL_CONNECTION_VERSION_TLS1_1); |
| } |
| |
| // Tests that the TLS 1.1 fallback, if enabled, triggers on closed connections. |
| TEST_F(HTTPSFallbackTest, TLSv1_1FallbackClosed) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_2; |
| ssl_options.tls_intolerance_type = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_CLOSE; |
| |
| set_fallback_min_version(SSL_PROTOCOL_VERSION_TLS1_1); |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| ExpectConnection(SSL_CONNECTION_VERSION_TLS1_1); |
| } |
| |
| // This test is disabled on Android because the remote test server doesn't cause |
| // a TCP reset. |
| #if !defined(OS_ANDROID) |
| // Tests fallback to TLS 1.1, if enabled, on connection reset. |
| TEST_F(HTTPSFallbackTest, TLSv1_1FallbackReset) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_2; |
| ssl_options.tls_intolerance_type = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_RESET; |
| |
| set_fallback_min_version(SSL_PROTOCOL_VERSION_TLS1_1); |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| ExpectConnection(SSL_CONNECTION_VERSION_TLS1_1); |
| } |
| #endif // !OS_ANDROID |
| |
| // Tests that we don't fallback, even if enabled, on handshake failure with |
| // servers that implement TLS_FALLBACK_SCSV. Also ensure that the original error |
| // code is reported. |
| TEST_F(HTTPSFallbackTest, FallbackSCSV) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| // Configure HTTPS server to be intolerant of TLS >= 1.1 in order to trigger |
| // a version fallback. |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_1; |
| // Have the server process TLS_FALLBACK_SCSV so that version fallback |
| // connections are rejected. |
| ssl_options.fallback_scsv_enabled = true; |
| |
| set_fallback_min_version(SSL_PROTOCOL_VERSION_TLS1_1); |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| |
| // ERR_SSL_VERSION_OR_CIPHER_MISMATCH is how the server simulates version |
| // intolerance. If the fallback SCSV is processed when the original error |
| // that caused the fallback should be returned, which should be |
| // ERR_SSL_VERSION_OR_CIPHER_MISMATCH. |
| ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); |
| } |
| |
| // Tests that we don't fallback, even if enabled, on connection closed with |
| // servers that implement TLS_FALLBACK_SCSV. Also ensure that the original error |
| // code is reported. |
| TEST_F(HTTPSFallbackTest, FallbackSCSVClosed) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| // Configure HTTPS server to be intolerant of TLS >= 1.1 in order to trigger |
| // a version fallback. |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_1; |
| ssl_options.tls_intolerance_type = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_CLOSE; |
| // Have the server process TLS_FALLBACK_SCSV so that version fallback |
| // connections are rejected. |
| ssl_options.fallback_scsv_enabled = true; |
| |
| set_fallback_min_version(SSL_PROTOCOL_VERSION_TLS1_1); |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_options)); |
| |
| // The original error should be replayed on rejected fallback. |
| ExpectFailure(ERR_CONNECTION_CLOSED); |
| } |
| |
| // Test that fallback probe connections don't cause sessions to be cached. |
| TEST_F(HTTPSRequestTest, FallbackProbeNoCache) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_OK); |
| ssl_options.tls_intolerant = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANT_TLS1_2; |
| ssl_options.tls_intolerance_type = |
| SpawnedTestServer::SSLOptions::TLS_INTOLERANCE_CLOSE; |
| ssl_options.record_resume = true; |
| |
| SpawnedTestServer test_server( |
| SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLClientSocket::ClearSessionCache(); |
| |
| // Make a connection that does a probe fallback to TLSv1.1 but fails because |
| // fallback is disabled. We don't wish a session for this connection to be |
| // inserted locally. |
| { |
| TestDelegate delegate; |
| FallbackTestURLRequestContext context(true); |
| |
| context.Init(); |
| scoped_ptr<URLRequest> request(context.CreateRequest( |
| test_server.GetURL("/"), DEFAULT_PRIORITY, &delegate)); |
| request->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, delegate.response_started_count()); |
| EXPECT_FALSE(request->status().is_success()); |
| EXPECT_EQ(URLRequestStatus::FAILED, request->status().status()); |
| EXPECT_EQ(ERR_SSL_FALLBACK_BEYOND_MINIMUM_VERSION, |
| request->status().error()); |
| } |
| |
| // Now allow TLSv1.1 fallback connections and request the session cache log. |
| { |
| TestDelegate delegate; |
| FallbackTestURLRequestContext context(true); |
| context.set_fallback_min_version(SSL_PROTOCOL_VERSION_TLS1_1); |
| |
| context.Init(); |
| scoped_ptr<URLRequest> request(context.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &delegate)); |
| request->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, delegate.response_started_count()); |
| EXPECT_NE(0, delegate.bytes_received()); |
| EXPECT_EQ( |
| SSL_CONNECTION_VERSION_TLS1_1, |
| SSLConnectionStatusToVersion(request->ssl_info().connection_status)); |
| EXPECT_TRUE(request->ssl_info().connection_status & |
| SSL_CONNECTION_VERSION_FALLBACK); |
| |
| std::vector<std::string> lines; |
| // If no sessions were cached then the server should have seen two sessions |
| // inserted with no lookups. |
| AssertTwoDistinctSessionsInserted(delegate.data_received()); |
| } |
| } |
| |
| class HTTPSSessionTest : public testing::Test { |
| public: |
| HTTPSSessionTest() : default_context_(true) { |
| cert_verifier_.set_default_result(OK); |
| |
| default_context_.set_network_delegate(&default_network_delegate_); |
| default_context_.set_cert_verifier(&cert_verifier_); |
| default_context_.Init(); |
| } |
| ~HTTPSSessionTest() override {} |
| |
| protected: |
| MockCertVerifier cert_verifier_; |
| TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. |
| TestURLRequestContext default_context_; |
| }; |
| |
| // Tests that session resumption is not attempted if an invalid certificate |
| // is presented. |
| TEST_F(HTTPSSessionTest, DontResumeSessionsForInvalidCertificates) { |
| SpawnedTestServer::SSLOptions ssl_options; |
| ssl_options.record_resume = true; |
| SpawnedTestServer test_server( |
| SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLClientSocket::ClearSessionCache(); |
| |
| // Simulate the certificate being expired and attempt a connection. |
| cert_verifier_.set_default_result(ERR_CERT_DATE_INVALID); |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| } |
| |
| reinterpret_cast<HttpCache*>(default_context_.http_transaction_factory())-> |
| CloseAllConnections(); |
| |
| // Now change the certificate to be acceptable (so that the response is |
| // loaded), and ensure that no session id is presented to the peer. |
| cert_verifier_.set_default_result(OK); |
| { |
| TestDelegate d; |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| test_server.GetURL("ssl-session-cache"), DEFAULT_PRIORITY, &d)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| // The response will look like; |
| // insert abc |
| // insert xyz |
| // |
| // With a newline at the end which makes the split think that there are |
| // three lines. |
| // |
| // If a session was presented (eg: a bug), then the response would look |
| // like; |
| // insert abc |
| // lookup abc |
| // insert xyz |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| AssertTwoDistinctSessionsInserted(d.data_received()); |
| } |
| } |
| |
| // This the fingerprint of the "Testing CA" certificate used by the testserver. |
| // See net/data/ssl/certificates/ocsp-test-root.pem. |
| static const SHA1HashValue kOCSPTestCertFingerprint = |
| { { 0xf1, 0xad, 0xf6, 0xce, 0x42, 0xac, 0xe7, 0xb4, 0xf4, 0x24, |
| 0xdb, 0x1a, 0xf7, 0xa0, 0x9f, 0x09, 0xa1, 0xea, 0xf1, 0x5c } }; |
| |
| // This is the SHA256, SPKI hash of the "Testing CA" certificate used by the |
| // testserver. |
| static const SHA256HashValue kOCSPTestCertSPKI = { { |
| 0xee, 0xe6, 0x51, 0x2d, 0x4c, 0xfa, 0xf7, 0x3e, |
| 0x6c, 0xd8, 0xca, 0x67, 0xed, 0xb5, 0x5d, 0x49, |
| 0x76, 0xe1, 0x52, 0xa7, 0x6e, 0x0e, 0xa0, 0x74, |
| 0x09, 0x75, 0xe6, 0x23, 0x24, 0xbd, 0x1b, 0x28, |
| } }; |
| |
| // This is the policy OID contained in the certificates that testserver |
| // generates. |
| static const char kOCSPTestCertPolicy[] = "1.3.6.1.4.1.11129.2.4.1"; |
| |
| class HTTPSOCSPTest : public HTTPSRequestTest { |
| public: |
| HTTPSOCSPTest() |
| : context_(true), |
| ev_test_policy_( |
| new ScopedTestEVPolicy(EVRootCAMetadata::GetInstance(), |
| kOCSPTestCertFingerprint, |
| kOCSPTestCertPolicy)) { |
| } |
| |
| void SetUp() override { |
| SetupContext(&context_); |
| context_.Init(); |
| |
| scoped_refptr<X509Certificate> root_cert = |
| ImportCertFromFile(GetTestCertsDirectory(), "ocsp-test-root.pem"); |
| CHECK_NE(static_cast<X509Certificate*>(NULL), root_cert.get()); |
| test_root_.reset(new ScopedTestRoot(root_cert.get())); |
| |
| #if defined(USE_NSS_VERIFIER) |
| SetURLRequestContextForNSSHttpIO(&context_); |
| EnsureNSSHttpIOInit(); |
| #endif |
| } |
| |
| void DoConnection(const SpawnedTestServer::SSLOptions& ssl_options, |
| CertStatus* out_cert_status) { |
| // We always overwrite out_cert_status. |
| *out_cert_status = 0; |
| SpawnedTestServer test_server( |
| SpawnedTestServer::TYPE_HTTPS, |
| ssl_options, |
| base::FilePath(FILE_PATH_LITERAL("net/data/ssl"))); |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| scoped_ptr<URLRequest> r( |
| context_.CreateRequest(test_server.GetURL("/"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| *out_cert_status = r->ssl_info().cert_status; |
| } |
| |
| ~HTTPSOCSPTest() override { |
| #if defined(USE_NSS_VERIFIER) |
| ShutdownNSSHttpIO(); |
| #endif |
| } |
| |
| protected: |
| // SetupContext configures the URLRequestContext that will be used for making |
| // connetions to testserver. This can be overridden in test subclasses for |
| // different behaviour. |
| virtual void SetupContext(URLRequestContext* context) { |
| context->set_ssl_config_service(new TestSSLConfigService( |
| true /* check for EV */, true /* online revocation checking */, |
| false /* require rev. checking for local |
| anchors */, |
| false /* token binding enabled */)); |
| } |
| |
| scoped_ptr<ScopedTestRoot> test_root_; |
| TestURLRequestContext context_; |
| scoped_ptr<ScopedTestEVPolicy> ev_test_policy_; |
| }; |
| |
| static CertStatus ExpectedCertStatusForFailedOnlineRevocationCheck() { |
| #if defined(OS_WIN) |
| // Windows can return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION but we don't |
| // have that ability on other platforms. |
| return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; |
| #else |
| return 0; |
| #endif |
| } |
| |
| // SystemSupportsHardFailRevocationChecking returns true iff the current |
| // operating system supports revocation checking and can distinguish between |
| // situations where a given certificate lacks any revocation information (eg: |
| // no CRLDistributionPoints and no OCSP Responder AuthorityInfoAccess) and when |
| // revocation information cannot be obtained (eg: the CRL was unreachable). |
| // If it does not, then tests which rely on 'hard fail' behaviour should be |
| // skipped. |
| static bool SystemSupportsHardFailRevocationChecking() { |
| #if defined(OS_WIN) || defined(USE_NSS_VERIFIER) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| // SystemUsesChromiumEVMetadata returns true iff the current operating system |
| // uses Chromium's EV metadata (i.e. EVRootCAMetadata). If it does not, then |
| // several tests are effected because our testing EV certificate won't be |
| // recognised as EV. |
| static bool SystemUsesChromiumEVMetadata() { |
| #if defined(USE_OPENSSL_CERTS) && !defined(OS_ANDROID) |
| // http://crbug.com/117478 - OpenSSL does not support EV validation. |
| return false; |
| #elif (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID) |
| // On OS X and Android, we use the system to tell us whether a certificate is |
| // EV or not and the system won't recognise our testing root. |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| static bool SystemSupportsOCSP() { |
| #if defined(USE_OPENSSL_CERTS) |
| // http://crbug.com/117478 - OpenSSL does not support OCSP. |
| return false; |
| #elif defined(OS_WIN) |
| return base::win::GetVersion() >= base::win::VERSION_VISTA; |
| #elif defined(OS_ANDROID) |
| // TODO(jnd): http://crbug.com/117478 - EV verification is not yet supported. |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| static bool SystemSupportsOCSPStapling() { |
| #if defined(USE_NSS_VERIFIER) |
| return true; |
| #elif defined(OS_WIN) |
| return base::win::GetVersion() >= base::win::VERSION_VISTA; |
| #else |
| return false; |
| #endif |
| } |
| |
| TEST_F(HTTPSOCSPTest, Valid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_OK; |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); |
| |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, Revoked) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_REVOKED; |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| #if !(defined(OS_MACOSX) && !defined(OS_IOS)) |
| // Doesn't pass on OS X yet for reasons that need to be investigated. |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| #endif |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, Invalid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(ExpectedCertStatusForFailedOnlineRevocationCheck(), |
| cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| // Without a positive OCSP response, we shouldn't show the EV status. |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, ValidStapled) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_OK; |
| ssl_options.staple_ocsp_response = true; |
| ssl_options.ocsp_server_unavailable = true; |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); |
| |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| // Disabled on NSS ports. See https://crbug.com/431716. |
| #if defined(USE_NSS_CERTS) |
| #define MAYBE_RevokedStapled DISABLED_RevokedStapled |
| #else |
| #define MAYBE_RevokedStapled RevokedStapled |
| #endif |
| TEST_F(HTTPSOCSPTest, MAYBE_RevokedStapled) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_REVOKED; |
| ssl_options.staple_ocsp_response = true; |
| ssl_options.ocsp_server_unavailable = true; |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| class HTTPSHardFailTest : public HTTPSOCSPTest { |
| protected: |
| void SetupContext(URLRequestContext* context) override { |
| context->set_ssl_config_service(new TestSSLConfigService( |
| false /* check for EV */, false /* online revocation checking */, |
| true /* require rev. checking for local |
| anchors */, |
| false /* token binding enabled */)); |
| } |
| }; |
| |
| TEST_F(HTTPSHardFailTest, FailsOnOCSPInvalid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, |
| cert_status & CERT_STATUS_REVOKED); |
| |
| // Without a positive OCSP response, we shouldn't show the EV status. |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| class HTTPSEVCRLSetTest : public HTTPSOCSPTest { |
| protected: |
| void SetupContext(URLRequestContext* context) override { |
| context->set_ssl_config_service(new TestSSLConfigService( |
| true /* check for EV */, false /* online revocation checking */, |
| false /* require rev. checking for local |
| anchors */, |
| false /* token binding enabled */)); |
| } |
| }; |
| |
| TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndInvalidOCSP) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| SSLConfigService::SetCRLSet(scoped_refptr<CRLSet>()); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(ExpectedCertStatusForFailedOnlineRevocationCheck(), |
| cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| |
| TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndRevokedOCSP) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_REVOKED; |
| SSLConfigService::SetCRLSet(scoped_refptr<CRLSet>()); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| // Currently only works for Windows. When using NSS or OS X, it's not |
| // possible to determine whether the check failed because of actual |
| // revocation or because there was an OCSP failure. |
| #if defined(OS_WIN) |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| #else |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| #endif |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| |
| TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndGoodOCSP) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_OK; |
| SSLConfigService::SetCRLSet(scoped_refptr<CRLSet>()); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| |
| TEST_F(HTTPSEVCRLSetTest, ExpiredCRLSet) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| SSLConfigService::SetCRLSet( |
| scoped_refptr<CRLSet>(CRLSet::ExpiredCRLSetForTesting())); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(ExpectedCertStatusForFailedOnlineRevocationCheck(), |
| cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| |
| TEST_F(HTTPSEVCRLSetTest, FreshCRLSetCovered) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| SSLConfigService::SetCRLSet( |
| scoped_refptr<CRLSet>(CRLSet::ForTesting( |
| false, &kOCSPTestCertSPKI, ""))); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| // With a fresh CRLSet that covers the issuing certificate, we shouldn't do a |
| // revocation check for EV. |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); |
| EXPECT_FALSE( |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| |
| TEST_F(HTTPSEVCRLSetTest, FreshCRLSetNotCovered) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| SSLConfigService::SetCRLSet( |
| scoped_refptr<CRLSet>(CRLSet::EmptyCRLSetForTesting())); |
| |
| CertStatus cert_status = 0; |
| DoConnection(ssl_options, &cert_status); |
| |
| // Even with a fresh CRLSet, we should still do online revocation checks when |
| // the certificate chain isn't covered by the CRLSet, which it isn't in this |
| // test. |
| EXPECT_EQ(ExpectedCertStatusForFailedOnlineRevocationCheck(), |
| cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| |
| TEST_F(HTTPSEVCRLSetTest, ExpiredCRLSetAndRevokedNonEVCert) { |
| // Test that when EV verification is requested, but online revocation |
| // checking is disabled, and the leaf certificate is not in fact EV, that |
| // no revocation checking actually happens. |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| // Unmark the certificate's OID as EV, which should disable revocation |
| // checking (as per the user preference) |
| ev_test_policy_.reset(); |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_REVOKED; |
| SSLConfigService::SetCRLSet( |
| scoped_refptr<CRLSet>(CRLSet::ExpiredCRLSetForTesting())); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| class HTTPSCRLSetTest : public HTTPSOCSPTest { |
| protected: |
| void SetupContext(URLRequestContext* context) override { |
| context->set_ssl_config_service(new TestSSLConfigService( |
| false /* check for EV */, false /* online revocation checking */, |
| false /* require rev. checking for local |
| anchors */, |
| false /* token binding enabled */)); |
| } |
| }; |
| |
| TEST_F(HTTPSCRLSetTest, ExpiredCRLSet) { |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_INVALID; |
| SSLConfigService::SetCRLSet( |
| scoped_refptr<CRLSet>(CRLSet::ExpiredCRLSetForTesting())); |
| |
| CertStatus cert_status; |
| DoConnection(ssl_options, &cert_status); |
| |
| // If we're not trying EV verification then, even if the CRLSet has expired, |
| // we don't fall back to online revocation checks. |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSCRLSetTest, CRLSetRevoked) { |
| #if defined(OS_ANDROID) |
| LOG(WARNING) << "Skipping test because system doesn't support CRLSets"; |
| return; |
| #endif |
| |
| SpawnedTestServer::SSLOptions ssl_options( |
| SpawnedTestServer::SSLOptions::CERT_AUTO); |
| ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_OK; |
| ssl_options.cert_serial = 10; |
| SSLConfigService::SetCRLSet( |
| scoped_refptr<CRLSet>(CRLSet::ForTesting( |
| false, &kOCSPTestCertSPKI, "\x0a"))); |
| |
| CertStatus cert_status = 0; |
| DoConnection(ssl_options, &cert_status); |
| |
| // If the certificate is recorded as revoked in the CRLSet, that should be |
| // reflected without online revocation checking. |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE( |
| static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED)); |
| } |
| #endif // !defined(OS_IOS) |
| |
| #if !defined(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) |
| // These tests aren't passing on Android. Either the RemoteTestServer isn't |
| // starting up successfully, or it can't access the test files. |
| // TODO(mmenke): Fix this. See http://crbug.com/495220 |
| class URLRequestTestFTP : public URLRequestTest { |
| public: |
| URLRequestTestFTP() |
| : ftp_transaction_factory_(&host_resolver_), |
| ftp_test_server_(SpawnedTestServer::TYPE_FTP, |
| SpawnedTestServer::kLocalhost, |
| base::FilePath(kTestFilePath)) { |
| // Can't use |default_context_|'s HostResolver to set up the |
| // FTPTransactionFactory because it hasn't been created yet. |
| default_context_.set_host_resolver(&host_resolver_); |
| } |
| |
| // URLRequestTest interface: |
| void SetUpFactory() override { |
| // Add FTP support to the default URLRequestContext. |
| job_factory_impl_->SetProtocolHandler( |
| "ftp", |
| make_scoped_ptr(new FtpProtocolHandler(&ftp_transaction_factory_))); |
| } |
| |
| std::string GetTestFileContents() { |
| base::FilePath path; |
| EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &path)); |
| path = path.Append(kTestFilePath); |
| path = path.AppendASCII(kFtpTestFile); |
| std::string contents; |
| EXPECT_TRUE(base::ReadFileToString(path, &contents)); |
| return contents; |
| } |
| |
| protected: |
| MockHostResolver host_resolver_; |
| FtpNetworkLayer ftp_transaction_factory_; |
| |
| SpawnedTestServer ftp_test_server_; |
| }; |
| |
| // Make sure an FTP request using an unsafe ports fails. |
| TEST_F(URLRequestTestFTP, UnsafePort) { |
| GURL url("ftp://127.0.0.1:7"); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r( |
| default_context_.CreateRequest(url, DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(URLRequestStatus::FAILED, r->status().status()); |
| EXPECT_EQ(ERR_UNSAFE_PORT, r->status().error()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPDirectoryListing) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURL("/"), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_LT(0, d.bytes_received()); |
| EXPECT_EQ(ftp_test_server_.host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(ftp_test_server_.host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPGetTestAnonymous) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURL(kFtpTestFile), DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d.data_received()); |
| EXPECT_EQ(ftp_test_server_.host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(ftp_test_server_.host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPGetTest) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "chrome", |
| "chrome"), |
| DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d.data_received()); |
| EXPECT_EQ(ftp_test_server_.host_port_pair().host(), |
| r->GetSocketAddress().host()); |
| EXPECT_EQ(ftp_test_server_.host_port_pair().port(), |
| r->GetSocketAddress().port()); |
| |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNoHttpResponse(load_timing_info); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPCheckWrongPassword) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "chrome", |
| "wrong_password"), |
| DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(d.bytes_received(), 0); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPCheckWrongPasswordRestart) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| // Set correct login credentials. The delegate will be asked for them when |
| // the initial login with wrong credentials will fail. |
| d.set_credentials(AuthCredentials(kChrome, kChrome)); |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "chrome", |
| "wrong_password"), |
| DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPCheckWrongUser) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "wrong_user", |
| "chrome"), |
| DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPCheckWrongUserRestart) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| TestDelegate d; |
| // Set correct login credentials. The delegate will be asked for them when |
| // the initial login with wrong credentials will fail. |
| d.set_credentials(AuthCredentials(kChrome, kChrome)); |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "wrong_user", |
| "chrome"), |
| DEFAULT_PRIORITY, &d)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPCacheURLCredentials) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| scoped_ptr<TestDelegate> d(new TestDelegate); |
| { |
| // Pass correct login identity in the URL. |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "chrome", |
| "chrome"), |
| DEFAULT_PRIORITY, d.get())); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d->response_started_count()); |
| EXPECT_FALSE(d->received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d->data_received()); |
| } |
| |
| d.reset(new TestDelegate); |
| { |
| // This request should use cached identity from previous request. |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURL(kFtpTestFile), DEFAULT_PRIORITY, d.get())); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d->response_started_count()); |
| EXPECT_FALSE(d->received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d->data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestFTP, FTPCacheLoginBoxCredentials) { |
| ASSERT_TRUE(ftp_test_server_.Start()); |
| |
| scoped_ptr<TestDelegate> d(new TestDelegate); |
| // Set correct login credentials. The delegate will be asked for them when |
| // the initial login with wrong credentials will fail. |
| d->set_credentials(AuthCredentials(kChrome, kChrome)); |
| { |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURLWithUserAndPassword(kFtpTestFile, "chrome", |
| "wrong_password"), |
| DEFAULT_PRIORITY, d.get())); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d->response_started_count()); |
| EXPECT_FALSE(d->received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d->data_received()); |
| } |
| |
| // Use a new delegate without explicit credentials. The cached ones should be |
| // used. |
| d.reset(new TestDelegate); |
| { |
| // Don't pass wrong credentials in the URL, they would override valid cached |
| // ones. |
| scoped_ptr<URLRequest> r(default_context_.CreateRequest( |
| ftp_test_server_.GetURL(kFtpTestFile), DEFAULT_PRIORITY, d.get())); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(r->is_pending()); |
| EXPECT_EQ(1, d->response_started_count()); |
| EXPECT_FALSE(d->received_data_before_response()); |
| EXPECT_EQ(GetTestFileContents(), d->data_received()); |
| } |
| } |
| #endif // !defined(DISABLE_FTP_SUPPORT) |
| |
| TEST_F(URLRequestTest, NetworkAccessedClearBeforeNetworkStart) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| d.set_quit_on_network_start(true); |
| |
| EXPECT_FALSE(req->response_info().network_accessed); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.received_before_network_start_count()); |
| EXPECT_EQ(0, d.response_started_count()); |
| EXPECT_FALSE(req->response_info().network_accessed); |
| |
| req->ResumeNetworkStart(); |
| base::RunLoop().Run(); |
| } |
| |
| TEST_F(URLRequestTest, NetworkAccessedClearOnDataRequest) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req( |
| default_context_.CreateRequest(GURL("data:,"), DEFAULT_PRIORITY, &d)); |
| |
| EXPECT_FALSE(req->response_info().network_accessed); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, default_network_delegate_.completed_requests()); |
| EXPECT_FALSE(req->response_info().network_accessed); |
| } |
| |
| TEST_F(URLRequestTest, NetworkAccessedSetOnHostResolutionFailure) { |
| MockHostResolver host_resolver; |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_host_resolver(&host_resolver); |
| host_resolver.rules()->AddSimulatedFailure("*"); |
| context.Init(); |
| |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(context.CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d)); |
| |
| EXPECT_FALSE(req->response_info().network_accessed); |
| |
| req->Start(); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(req->response_info().network_accessed); |
| } |
| |
| // Test that URLRequest is canceled correctly. |
| // See http://crbug.com/508900 |
| TEST_F(URLRequestTest, URLRequestRedirectJobCancelRequest) { |
| TestDelegate d; |
| scoped_ptr<URLRequest> req(default_context_.CreateRequest( |
| GURL("http://not-a-real-domain/"), DEFAULT_PRIORITY, &d)); |
| |
| scoped_ptr<URLRequestRedirectJob> job(new URLRequestRedirectJob( |
| req.get(), &default_network_delegate_, |
| GURL("http://this-should-never-be-navigated-to/"), |
| URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, "Jumbo shrimp")); |
| AddTestInterceptor()->set_main_intercept_job(std::move(job)); |
| |
| req->Start(); |
| req->Cancel(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| } // namespace net |