diff options
-rw-r--r-- | chrome/browser/policy/cloud_policy_controller.h | 2 | ||||
-rw-r--r-- | chrome/browser/policy/device_management_service.cc | 53 | ||||
-rw-r--r-- | chrome/browser/policy/device_management_service_unittest.cc | 74 | ||||
-rw-r--r-- | content/common/net/url_fetcher.cc | 9 | ||||
-rw-r--r-- | content/common/net/url_fetcher.h | 6 | ||||
-rw-r--r-- | content/test/test_url_fetcher_factory.cc | 10 | ||||
-rw-r--r-- | content/test/test_url_fetcher_factory.h | 4 | ||||
-rwxr-xr-x | net/tools/testserver/testserver.py | 2 |
8 files changed, 146 insertions, 14 deletions
diff --git a/chrome/browser/policy/cloud_policy_controller.h b/chrome/browser/policy/cloud_policy_controller.h index c446704..b7226e4 100644 --- a/chrome/browser/policy/cloud_policy_controller.h +++ b/chrome/browser/policy/cloud_policy_controller.h @@ -37,7 +37,7 @@ class CloudPolicyController // Sets the refresh rate at which to re-fetch policy information. void SetRefreshRate(int64 refresh_rate_milliseconds); - // Triggers an immediate retry of of the current operation. + // Triggers an immediate retry of the current operation. void Retry(); // Stops any pending activity and resets the controller to unenrolled state. diff --git a/chrome/browser/policy/device_management_service.cc b/chrome/browser/policy/device_management_service.cc index 9a51ae7..198cae7 100644 --- a/chrome/browser/policy/device_management_service.cc +++ b/chrome/browser/policy/device_management_service.cc @@ -17,6 +17,7 @@ #include "net/base/net_errors.h" #include "net/base/ssl_config_service_defaults.h" #include "net/http/http_network_layer.h" +#include "net/http/http_response_headers.h" #include "net/proxy/proxy_service.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" @@ -27,6 +28,26 @@ namespace policy { namespace { +bool IsProxyError(const net::URLRequestStatus status) { + switch (status.error()) { + case net::ERR_PROXY_CONNECTION_FAILED: + case net::ERR_TUNNEL_CONNECTION_FAILED: + case net::ERR_PROXY_AUTH_UNSUPPORTED: + case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE: + case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED: + case net::ERR_PROXY_CERTIFICATE_INVALID: + case net::ERR_SOCKS_CONNECTION_FAILED: + case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: + return true; + } + return false; +} + +bool IsProtobufMimeType(const URLFetcher* source) { + return source->response_headers()->HasHeaderValue( + "content-type", "application/x-protobuffer"); +} + // Custom request context implementation that allows to override the user agent, // amongst others. Wraps a baseline request context from which we reuse the // networking components. @@ -214,19 +235,25 @@ void DeviceManagementService::OnURLFetchComplete( // Retry the job if it failed due to a broken proxy, by bypassing the // proxy on the next try. Don't retry if this URLFetcher already bypassed // the proxy. - int error = status.error(); - if (!status.is_success() && - ((source->load_flags() & net::LOAD_BYPASS_PROXY) == 0) && - (error == net::ERR_PROXY_CONNECTION_FAILED || - error == net::ERR_TUNNEL_CONNECTION_FAILED || - error == net::ERR_PROXY_AUTH_UNSUPPORTED || - error == net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE || - error == net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED || - error == net::ERR_PROXY_CERTIFICATE_INVALID || - error == net::ERR_SOCKS_CONNECTION_FAILED || - error == net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE)) { - LOG(WARNING) << "Proxy failed while contacting dmserver. Retrying " - << "without using the proxy."; + bool retry = false; + if ((source->load_flags() & net::LOAD_BYPASS_PROXY) == 0) { + if (!status.is_success() && IsProxyError(status)) { + LOG(WARNING) << "Proxy failed while contacting dmserver."; + retry = true; + } else if (status.is_success() && + source->was_fetched_via_proxy() && + !IsProtobufMimeType(source)) { + // The proxy server can be misconfigured but pointing to an existing + // server that replies to requests. Try to recover if a successful + // request that went through a proxy returns an unexpected mime type. + LOG(WARNING) << "Got bad mime-type in response from dmserver that was " + << "fetched via a proxy."; + retry = true; + } + } + + if (retry) { + LOG(WARNING) << "Retrying dmserver request without using a proxy."; StartJob(job, true); } else { job->HandleResponse(status, response_code, cookies, data); diff --git a/chrome/browser/policy/device_management_service_unittest.cc b/chrome/browser/policy/device_management_service_unittest.cc index 2d73098..a09f46f 100644 --- a/chrome/browser/policy/device_management_service_unittest.cc +++ b/chrome/browser/policy/device_management_service_unittest.cc @@ -15,6 +15,9 @@ #include "content/browser/browser_thread.h" #include "content/test/test_url_fetcher_factory.h" #include "net/base/escape.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" #include "net/url_request/url_request_status.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -521,4 +524,75 @@ TEST_F(DeviceManagementServiceTest, CancelDuringCallback) { EXPECT_FALSE(backend_.get()); } +TEST_F(DeviceManagementServiceTest, RetryOnProxyError) { + // Make a request. + DeviceRegisterResponseDelegateMock mock; + EXPECT_CALL(mock, HandleRegisterResponse(_)).Times(0); + EXPECT_CALL(mock, OnError(_)).Times(0); + + em::DeviceRegisterRequest request; + backend_->ProcessRegisterRequest(kGaiaAuthToken, kOAuthToken, + kDeviceId, request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + EXPECT_TRUE((fetcher->load_flags() & net::LOAD_BYPASS_PROXY) == 0); + const GURL original_url(fetcher->original_url()); + const std::string upload_data(fetcher->upload_data()); + + // Generate a callback with a proxy failure. + net::URLRequestStatus status(net::URLRequestStatus::FAILED, + net::ERR_PROXY_CONNECTION_FAILED); + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceUrl), + status, + 0, + net::ResponseCookies(), + ""); + + // Verify that a new URLFetcher was started that bypasses the proxy. + fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + EXPECT_TRUE(fetcher->load_flags() & net::LOAD_BYPASS_PROXY); + EXPECT_EQ(original_url, fetcher->original_url()); + EXPECT_EQ(upload_data, fetcher->upload_data()); +} + +TEST_F(DeviceManagementServiceTest, RetryOnBadResponseFromProxy) { + // Make a request. + DeviceRegisterResponseDelegateMock mock; + EXPECT_CALL(mock, HandleRegisterResponse(_)).Times(0); + EXPECT_CALL(mock, OnError(_)).Times(0); + + em::DeviceRegisterRequest request; + backend_->ProcessRegisterRequest(kGaiaAuthToken, kOAuthToken, + kDeviceId, request, &mock); + TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + EXPECT_TRUE((fetcher->load_flags() & net::LOAD_BYPASS_PROXY) == 0); + const GURL original_url(fetcher->original_url()); + const std::string upload_data(fetcher->upload_data()); + fetcher->set_was_fetched_via_proxy(true); + scoped_refptr<net::HttpResponseHeaders> headers; + headers = new net::HttpResponseHeaders( + "HTTP/1.1 200 OK\0Content-type: bad/type\0\0"); + fetcher->set_response_headers(headers); + + // Generate a callback with a valid http response, that was generated by + // a bad/wrong proxy. + net::URLRequestStatus status; + fetcher->delegate()->OnURLFetchComplete(fetcher, + GURL(kServiceUrl), + status, + 200, + net::ResponseCookies(), + ""); + + // Verify that a new URLFetcher was started that bypasses the proxy. + fetcher = factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + EXPECT_TRUE((fetcher->load_flags() & net::LOAD_BYPASS_PROXY) != 0); + EXPECT_EQ(original_url, fetcher->original_url()); + EXPECT_EQ(upload_data, fetcher->upload_data()); +} + } // namespace policy diff --git a/content/common/net/url_fetcher.cc b/content/common/net/url_fetcher.cc index be4d448..677eafe 100644 --- a/content/common/net/url_fetcher.cc +++ b/content/common/net/url_fetcher.cc @@ -1007,6 +1007,11 @@ net::HttpResponseHeaders* URLFetcher::response_headers() const { return core_->response_headers_; } +void URLFetcher::set_response_headers( + scoped_refptr<net::HttpResponseHeaders> headers) { + core_->response_headers_ = headers; +} + // TODO(panayiotis): socket_address_ is written in the IO thread, // if this is accessed in the UI thread, this could result in a race. // Same for response_headers_ above and was_fetched_via_proxy_ below. @@ -1018,6 +1023,10 @@ bool URLFetcher::was_fetched_via_proxy() const { return core_->was_fetched_via_proxy_; } +void URLFetcher::set_was_fetched_via_proxy(bool flag) { + core_->was_fetched_via_proxy_ = flag; +} + void URLFetcher::Start() { core_->Start(); } diff --git a/content/common/net/url_fetcher.h b/content/common/net/url_fetcher.h index 1e58a1d..74bbf7b 100644 --- a/content/common/net/url_fetcher.h +++ b/content/common/net/url_fetcher.h @@ -283,6 +283,12 @@ class CONTENT_EXPORT URLFetcher { // Used by tests. const std::string& upload_data() const; + // Used by tests. + void set_was_fetched_via_proxy(bool flag); + + // Used by tests. + void set_response_headers(scoped_refptr<net::HttpResponseHeaders> headers); + virtual void SetResponseDestinationForTesting(ResponseDestinationType); virtual ResponseDestinationType GetResponseDestinationForTesting() const; diff --git a/content/test/test_url_fetcher_factory.cc b/content/test/test_url_fetcher_factory.cc index b079b3e..f6ebe94 100644 --- a/content/test/test_url_fetcher_factory.cc +++ b/content/test/test_url_fetcher_factory.cc @@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "base/message_loop.h" +#include "net/http/http_response_headers.h" #include "net/url_request/url_request_status.h" ScopedURLFetcherFactory::ScopedURLFetcherFactory(URLFetcher::Factory* factory) { @@ -48,6 +49,15 @@ void TestURLFetcher::set_status(const net::URLRequestStatus& status) { fake_status_ = status; } +void TestURLFetcher::set_was_fetched_via_proxy(bool flag) { + URLFetcher::set_was_fetched_via_proxy(flag); +} + +void TestURLFetcher::set_response_headers( + scoped_refptr<net::HttpResponseHeaders> headers) { + URLFetcher::set_response_headers(headers); +} + void TestURLFetcher::SetResponseString(const std::string& response) { SetResponseDestinationForTesting(STRING); fake_response_string_ = response; diff --git a/content/test/test_url_fetcher_factory.h b/content/test/test_url_fetcher_factory.h index c6cd0aa..377b5b0 100644 --- a/content/test/test_url_fetcher_factory.h +++ b/content/test/test_url_fetcher_factory.h @@ -96,6 +96,10 @@ class TestURLFetcher : public URLFetcher { } virtual int response_code() const OVERRIDE; + void set_was_fetched_via_proxy(bool flag); + + void set_response_headers(scoped_refptr<net::HttpResponseHeaders> headers); + // Set string data. void SetResponseString(const std::string& response); diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index 0849a29..4147004 100755 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py @@ -1418,6 +1418,8 @@ class TestPageHandler(BasePageHandler): self.headers, raw_request)) self.send_response(http_response) + if (http_response == 200): + self.send_header('Content-type', 'application/x-protobuffer') self.end_headers() self.wfile.write(raw_reply) return True |