summaryrefslogtreecommitdiffstats
path: root/net/url_request
diff options
context:
space:
mode:
Diffstat (limited to 'net/url_request')
-rw-r--r--net/url_request/url_request_context.cc4
-rw-r--r--net/url_request/url_request_context.h8
-rw-r--r--net/url_request/url_request_context_storage.cc7
-rw-r--r--net/url_request/url_request_context_storage.h3
-rw-r--r--net/url_request/url_request_job_factory.cc93
-rw-r--r--net/url_request/url_request_job_factory.h96
-rw-r--r--net/url_request/url_request_job_factory_unittest.cc152
-rw-r--r--net/url_request/url_request_job_manager.cc32
-rw-r--r--net/url_request/url_request_test_util.cc2
9 files changed, 394 insertions, 3 deletions
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 0057f2a..c9c232d 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -24,7 +24,8 @@ URLRequestContext::URLRequestContext()
network_delegate_(NULL),
transport_security_state_(NULL),
http_transaction_factory_(NULL),
- ftp_transaction_factory_(NULL) {
+ ftp_transaction_factory_(NULL),
+ job_factory_(NULL) {
}
void URLRequestContext::CopyFrom(URLRequestContext* other) {
@@ -47,6 +48,7 @@ void URLRequestContext::CopyFrom(URLRequestContext* other) {
set_referrer_charset(other->referrer_charset());
set_http_transaction_factory(other->http_transaction_factory());
set_ftp_transaction_factory(other->ftp_transaction_factory());
+ set_job_factory(other->job_factory());
}
void URLRequestContext::set_cookie_store(CookieStore* cookie_store) {
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index bc74459..8749573 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -32,6 +32,7 @@ class NetworkDelegate;
class ProxyService;
class SSLConfigService;
class URLRequest;
+class URLRequestJobFactory;
// Subclass to provide application-specific context for URLRequest
// instances. Note that URLRequestContext typically does not provide storage for
@@ -172,6 +173,11 @@ class URLRequestContext
bool is_main() const { return is_main_; }
void set_is_main(bool is_main) { is_main_ = is_main; }
+ const URLRequestJobFactory* job_factory() const { return job_factory_; }
+ void set_job_factory(const URLRequestJobFactory* job_factory) {
+ job_factory_ = job_factory;
+ }
+
protected:
friend class base::RefCountedThreadSafe<URLRequestContext>;
@@ -206,9 +212,9 @@ class URLRequestContext
// used in communication with a server but is used to construct a suggested
// filename for file download.
std::string referrer_charset_;
-
HttpTransactionFactory* http_transaction_factory_;
FtpTransactionFactory* ftp_transaction_factory_;
+ const URLRequestJobFactory* job_factory_;
// ---------------------------------------------------------------------------
// Important: When adding any new members below, consider whether they need to
diff --git a/net/url_request/url_request_context_storage.cc b/net/url_request/url_request_context_storage.cc
index d54614a..7531fd7 100644
--- a/net/url_request/url_request_context_storage.cc
+++ b/net/url_request/url_request_context_storage.cc
@@ -16,6 +16,7 @@
#include "net/http/http_transaction_factory.h"
#include "net/proxy/proxy_service.h"
#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory.h"
namespace net {
@@ -99,4 +100,10 @@ void URLRequestContextStorage::set_ftp_transaction_factory(
ftp_transaction_factory_.reset(ftp_transaction_factory);
}
+void URLRequestContextStorage::set_job_factory(
+ URLRequestJobFactory* job_factory) {
+ context_->set_job_factory(job_factory);
+ job_factory_.reset(job_factory);
+}
+
} // namespace net
diff --git a/net/url_request/url_request_context_storage.h b/net/url_request/url_request_context_storage.h
index ccb042e..4cdfc43 100644
--- a/net/url_request/url_request_context_storage.h
+++ b/net/url_request/url_request_context_storage.h
@@ -26,6 +26,7 @@ class ProxyService;
class SSLConfigService;
class TransportSecurityState;
class URLRequestContext;
+class URLRequestJobFactory;
// URLRequestContextStorage is a helper class that provides storage for unowned
// member variables of URLRequestContext.
@@ -57,6 +58,7 @@ class URLRequestContextStorage {
HttpTransactionFactory* http_transaction_factory);
void set_ftp_transaction_factory(
FtpTransactionFactory* ftp_transaction_factory);
+ void set_job_factory(URLRequestJobFactory* job_factory);
private:
// We use a raw pointer to prevent reference cycles, since
@@ -80,6 +82,7 @@ class URLRequestContextStorage {
scoped_ptr<HttpTransactionFactory> http_transaction_factory_;
scoped_ptr<FtpTransactionFactory> ftp_transaction_factory_;
+ scoped_ptr<URLRequestJobFactory> job_factory_;
DISALLOW_COPY_AND_ASSIGN(URLRequestContextStorage);
};
diff --git a/net/url_request/url_request_job_factory.cc b/net/url_request/url_request_job_factory.cc
new file mode 100644
index 0000000..78fcb38
--- /dev/null
+++ b/net/url_request/url_request_job_factory.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2011 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 "net/url_request/url_request_job_factory.h"
+
+#include "base/stl_util-inl.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_job_manager.h"
+
+namespace net {
+
+URLRequestJobFactory::ProtocolHandler::~ProtocolHandler() {}
+
+URLRequestJobFactory::Interceptor::~Interceptor() {}
+
+URLRequestJobFactory::URLRequestJobFactory() {}
+
+URLRequestJobFactory::~URLRequestJobFactory() {
+ STLDeleteValues(&protocol_handler_map_);
+ STLDeleteElements(&interceptors_);
+}
+
+bool URLRequestJobFactory::SetProtocolHandler(
+ const std::string& scheme,
+ ProtocolHandler* protocol_handler) {
+ DCHECK(CalledOnValidThread());
+
+ if (!protocol_handler) {
+ ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
+ if (it == protocol_handler_map_.end())
+ return false;
+
+ delete it->second;
+ protocol_handler_map_.erase(it);
+ return true;
+ }
+
+ if (ContainsKey(protocol_handler_map_, scheme))
+ return false;
+ protocol_handler_map_[scheme] = protocol_handler;
+ return true;
+}
+
+void URLRequestJobFactory::AddInterceptor(Interceptor* interceptor) {
+ DCHECK(CalledOnValidThread());
+ CHECK(interceptor);
+
+ interceptors_.push_back(interceptor);
+}
+
+URLRequestJob* URLRequestJobFactory::MaybeCreateJobWithInterceptor(
+ URLRequest* request) const {
+ DCHECK(CalledOnValidThread());
+ URLRequestJob* job = NULL;
+
+ if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
+ InterceptorList::const_iterator i;
+ for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
+ job = (*i)->MaybeIntercept(request);
+ if (job)
+ return job;
+ }
+ }
+ return NULL;
+}
+
+URLRequestJob* URLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ URLRequest* request) const {
+ DCHECK(CalledOnValidThread());
+ ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
+ if (it == protocol_handler_map_.end())
+ return NULL;
+ return it->second->MaybeCreateJob(request);
+}
+
+bool URLRequestJobFactory::IsHandledProtocol(const std::string& scheme) const {
+ DCHECK(CalledOnValidThread());
+ return ContainsKey(protocol_handler_map_, scheme) ||
+ URLRequestJobManager::GetInstance()->SupportsScheme(scheme);
+}
+
+bool URLRequestJobFactory::IsHandledURL(const GURL& url) const {
+ if (!url.is_valid()) {
+ // We handle error cases.
+ return true;
+ }
+ return IsHandledProtocol(url.scheme());
+}
+
+} // namespace net
diff --git a/net/url_request/url_request_job_factory.h b/net/url_request/url_request_job_factory.h
new file mode 100644
index 0000000..594883a
--- /dev/null
+++ b/net/url_request/url_request_job_factory.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 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.
+
+#ifndef NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
+#define NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/threading/non_thread_safe.h"
+
+class GURL;
+
+namespace net {
+
+class URLRequest;
+class URLRequestJob;
+
+class URLRequestJobFactory : public base::NonThreadSafe {
+ public:
+ class ProtocolHandler {
+ public:
+ virtual ~ProtocolHandler();
+
+ virtual URLRequestJob* MaybeCreateJob(URLRequest* request) const = 0;
+ };
+
+ class Interceptor {
+ public:
+ virtual ~Interceptor();
+
+ // Called for every request made. Should return a new job to handle the
+ // request if it should be intercepted, or NULL to allow the request to
+ // be handled in the normal manner.
+ virtual URLRequestJob* MaybeIntercept(URLRequest* request) const = 0;
+
+ // Called after having received a redirect response, but prior to the
+ // the request delegate being informed of the redirect. Can return a new
+ // job to replace the existing job if it should be intercepted, or NULL
+ // to allow the normal handling to continue. If a new job is provided,
+ // the delegate never sees the original redirect response, instead the
+ // response produced by the intercept job will be returned.
+ virtual URLRequestJob* MaybeInterceptRedirect(
+ const GURL& location,
+ URLRequest* request) const = 0;
+
+ // Called after having received a final response, but prior to the
+ // the request delegate being informed of the response. This is also
+ // called when there is no server response at all to allow interception
+ // on DNS or network errors. Can return a new job to replace the existing
+ // job if it should be intercepted, or NULL to allow the normal handling to
+ // continue. If a new job is provided, the delegate never sees the original
+ // response, instead the response produced by the intercept job will be
+ // returned.
+ virtual URLRequestJob* MaybeInterceptResponse(
+ URLRequest* request) const = 0;
+ };
+
+ URLRequestJobFactory();
+ ~URLRequestJobFactory();
+
+ // Sets the ProtocolHandler for a scheme. Returns true on success, false on
+ // failure (a ProtocolHandler already exists for |scheme|). On success,
+ // URLRequestJobFactory takes ownership of |protocol_handler|.
+ bool SetProtocolHandler(const std::string& scheme,
+ ProtocolHandler* protocol_handler);
+
+ // Takes ownership of |interceptor|. Adds it to the end of the Interceptor
+ // list.
+ void AddInterceptor(Interceptor* interceptor);
+
+ URLRequestJob* MaybeCreateJobWithInterceptor(URLRequest* request) const;
+
+ URLRequestJob* MaybeCreateJobWithProtocolHandler(const std::string& scheme,
+ URLRequest* request) const;
+
+ bool IsHandledProtocol(const std::string& scheme) const;
+
+ bool IsHandledURL(const GURL& url) const;
+
+ private:
+ typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
+ typedef std::vector<Interceptor*> InterceptorList;
+
+ ProtocolHandlerMap protocol_handler_map_;
+ InterceptorList interceptors_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestJobFactory);
+};
+
+} // namespace net
+
+#endif // NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
diff --git a/net/url_request/url_request_job_factory_unittest.cc b/net/url_request/url_request_job_factory_unittest.cc
new file mode 100644
index 0000000..46c952c
--- /dev/null
+++ b/net/url_request/url_request_job_factory_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2011 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 "net/url_request/url_request_job_factory.h"
+
+#include "base/task.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class MockURLRequestJob : public URLRequestJob {
+ public:
+ MockURLRequestJob(URLRequest* request, const URLRequestStatus& status)
+ : URLRequestJob(request),
+ status_(status),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
+
+ virtual void Start() {
+ // Start reading asynchronously so that all error reporting and data
+ // callbacks happen as they would for network requests.
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&MockURLRequestJob::StartAsync));
+ }
+
+ private:
+ void StartAsync() {
+ SetStatus(status_);
+ NotifyHeadersComplete();
+ }
+
+ URLRequestStatus status_;
+ ScopedRunnableMethodFactory<MockURLRequestJob> method_factory_;
+};
+
+class DummyProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
+ public:
+ virtual URLRequestJob* MaybeCreateJob(URLRequest* request) const {
+ return new MockURLRequestJob(
+ request, URLRequestStatus(URLRequestStatus::SUCCESS, OK));
+ }
+};
+
+class DummyInterceptor : public URLRequestJobFactory::Interceptor {
+ public:
+ virtual URLRequestJob* MaybeIntercept(URLRequest* request) const {
+ return new MockURLRequestJob(
+ request,
+ URLRequestStatus(URLRequestStatus::FAILED, ERR_FAILED));
+ }
+
+ virtual URLRequestJob* MaybeInterceptRedirect(
+ const GURL& /* location */,
+ URLRequest* /* request */) const {
+ return NULL;
+ }
+
+ virtual URLRequestJob* MaybeInterceptResponse(
+ URLRequest* /* request */) const {
+ return NULL;
+ }
+};
+
+TEST(URLRequestJobFactoryTest, NoProtocolHandler) {
+ TestDelegate delegate;
+ scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+ TestURLRequest request(GURL("foo://bar"), &delegate);
+ request.set_context(request_context);
+ request.Start();
+
+ MessageLoop::current()->Run();
+ EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+ EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, BasicProtocolHandler) {
+ TestDelegate delegate;
+ scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+ URLRequestJobFactory job_factory;
+ request_context->set_job_factory(&job_factory);
+ job_factory.SetProtocolHandler("foo", new DummyProtocolHandler);
+ TestURLRequest request(GURL("foo://bar"), &delegate);
+ request.set_context(request_context);
+ request.Start();
+
+ MessageLoop::current()->Run();
+ EXPECT_EQ(URLRequestStatus::SUCCESS, request.status().status());
+ EXPECT_EQ(OK, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, DeleteProtocolHandler) {
+ scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+ URLRequestJobFactory job_factory;
+ request_context->set_job_factory(&job_factory);
+ job_factory.SetProtocolHandler("foo", new DummyProtocolHandler);
+ job_factory.SetProtocolHandler("foo", NULL);
+}
+
+TEST(URLRequestJobFactoryTest, BasicInterceptor) {
+ TestDelegate delegate;
+ scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+ URLRequestJobFactory job_factory;
+ request_context->set_job_factory(&job_factory);
+ job_factory.AddInterceptor(new DummyInterceptor);
+ TestURLRequest request(GURL("http://bar"), &delegate);
+ request.set_context(request_context);
+ request.Start();
+
+ MessageLoop::current()->Run();
+ EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+ EXPECT_EQ(ERR_FAILED, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, InterceptorNeedsValidSchemeStill) {
+ TestDelegate delegate;
+ scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+ URLRequestJobFactory job_factory;
+ request_context->set_job_factory(&job_factory);
+ job_factory.AddInterceptor(new DummyInterceptor);
+ TestURLRequest request(GURL("foo://bar"), &delegate);
+ request.set_context(request_context);
+ request.Start();
+
+ MessageLoop::current()->Run();
+ EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+ EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, InterceptorOverridesProtocolHandler) {
+ TestDelegate delegate;
+ scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+ URLRequestJobFactory job_factory;
+ request_context->set_job_factory(&job_factory);
+ job_factory.SetProtocolHandler("foo", new DummyProtocolHandler);
+ job_factory.AddInterceptor(new DummyInterceptor);
+ TestURLRequest request(GURL("foo://bar"), &delegate);
+ request.set_context(request_context);
+ request.Start();
+
+ MessageLoop::current()->Run();
+ EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+ EXPECT_EQ(ERR_FAILED, request.status().os_error());
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/url_request/url_request_job_manager.cc b/net/url_request/url_request_job_manager.cc
index 06891b4..dc6af79 100644
--- a/net/url_request/url_request_job_manager.cc
+++ b/net/url_request/url_request_job_manager.cc
@@ -11,12 +11,15 @@
#include "base/string_util.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
+#include "net/base/network_delegate.h"
#include "net/url_request/url_request_about_job.h"
+#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_data_job.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_file_job.h"
#include "net/url_request/url_request_ftp_job.h"
#include "net/url_request/url_request_http_job.h"
+#include "net/url_request/url_request_job_factory.h"
namespace net {
@@ -55,15 +58,33 @@ URLRequestJob* URLRequestJobManager::CreateJob(
return new URLRequestErrorJob(request, ERR_INVALID_URL);
// We do this here to avoid asking interceptors about unsupported schemes.
+ const URLRequestJobFactory* job_factory = NULL;
+ if (request->context())
+ job_factory = request->context()->job_factory();
+
const std::string& scheme = request->url().scheme(); // already lowercase
- if (!SupportsScheme(scheme))
+ if (job_factory) {
+ if (!job_factory->IsHandledProtocol(scheme)) {
+ return new URLRequestErrorJob(request, ERR_UNKNOWN_URL_SCHEME);
+ }
+ } else if (!SupportsScheme(scheme)) {
return new URLRequestErrorJob(request, ERR_UNKNOWN_URL_SCHEME);
+ }
// THREAD-SAFETY NOTICE:
// We do not need to acquire the lock here since we are only reading our
// data structures. They should only be modified on the current thread.
// See if the request should be intercepted.
+ //
+
+ if (job_factory) {
+ URLRequestJob* job = job_factory->MaybeCreateJobWithInterceptor(request);
+ if (job)
+ return job;
+ }
+
+ // TODO(willchan): Remove this in favor of URLRequestJobFactory::Interceptor.
if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
InterceptorList::const_iterator i;
for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
@@ -73,6 +94,15 @@ URLRequestJob* URLRequestJobManager::CreateJob(
}
}
+ if (job_factory) {
+ URLRequestJob* job =
+ job_factory->MaybeCreateJobWithProtocolHandler(scheme, request);
+ if (job)
+ return job;
+ }
+
+ // TODO(willchan): Remove this in favor of
+ // URLRequestJobFactory::ProtocolHandler.
// See if the request should be handled by a registered protocol factory.
// If the registered factory returns null, then we want to fall-back to the
// built-in protocol factory.
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 680f12d..fff1d8b 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -10,6 +10,7 @@
#include "base/threading/thread.h"
#include "net/base/host_port_pair.h"
#include "net/http/http_network_session.h"
+#include "net/url_request/url_request_job_factory.h"
TestURLRequestContext::TestURLRequestContext()
: ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
@@ -67,6 +68,7 @@ void TestURLRequestContext::Init() {
context_storage_.set_cookie_store(new net::CookieMonster(NULL, NULL));
set_accept_language("en-us,fr");
set_accept_charset("iso-8859-1,*,utf-8");
+ context_storage_.set_job_factory(new net::URLRequestJobFactory);
}