summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/policy/cloud_policy_controller.h2
-rw-r--r--chrome/browser/policy/device_management_service.cc53
-rw-r--r--chrome/browser/policy/device_management_service_unittest.cc74
-rw-r--r--content/common/net/url_fetcher.cc9
-rw-r--r--content/common/net/url_fetcher.h6
-rw-r--r--content/test/test_url_fetcher_factory.cc10
-rw-r--r--content/test/test_url_fetcher_factory.h4
-rwxr-xr-xnet/tools/testserver/testserver.py2
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