summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-05 09:27:30 +0000
committerdarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-05 09:27:30 +0000
commitdd6ee450f95406f6d033dbe716229078adfc000b (patch)
tree4151d9595c4d57e9569f1d97090533dfdc101594
parent2d6251ff1c9ce19585c57e2ee693e3f035ada062 (diff)
downloadchromium_src-dd6ee450f95406f6d033dbe716229078adfc000b.zip
chromium_src-dd6ee450f95406f6d033dbe716229078adfc000b.tar.gz
chromium_src-dd6ee450f95406f6d033dbe716229078adfc000b.tar.bz2
Mojo: Initial network service w/ URLLoader interface.
BUG=378150 Review URL: https://codereview.chromium.org/306143002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275047 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--mojo/examples/wget/wget.cc116
-rw-r--r--mojo/mojo.gyp2
-rw-r--r--mojo/mojo_examples.gypi22
-rw-r--r--mojo/mojo_services.gypi42
-rw-r--r--mojo/public/cpp/application/lib/mojo_main_chromium.cc4
-rw-r--r--mojo/public/cpp/bindings/lib/connector.cc1
-rw-r--r--mojo/services/network/DEPS4
-rw-r--r--mojo/services/network/main.cc30
-rw-r--r--mojo/services/network/network_context.cc123
-rw-r--r--mojo/services/network/network_context.h42
-rw-r--r--mojo/services/network/network_service_impl.cc23
-rw-r--r--mojo/services/network/network_service_impl.h29
-rw-r--r--mojo/services/network/url_loader_impl.cc232
-rw-r--r--mojo/services/network/url_loader_impl.h60
-rw-r--r--mojo/services/public/interfaces/network/network_error.mojom12
-rw-r--r--mojo/services/public/interfaces/network/network_service.mojom15
-rw-r--r--mojo/services/public/interfaces/network/url_loader.mojom67
17 files changed, 821 insertions, 3 deletions
diff --git a/mojo/examples/wget/wget.cc b/mojo/examples/wget/wget.cc
new file mode 100644
index 0000000..e470291
--- /dev/null
+++ b/mojo/examples/wget/wget.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 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 <stdio.h>
+
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class WGetApp : public Application, public URLLoaderClient {
+ public:
+ virtual void Initialize() MOJO_OVERRIDE {
+ ConnectTo("mojo:mojo_network_service", &network_service_);
+ Start();
+ }
+
+ private:
+ virtual void OnReceivedRedirect(URLResponsePtr response,
+ const String& new_url,
+ const String& new_method) MOJO_OVERRIDE {
+ assert(false); // This should never be called.
+ }
+
+ virtual void OnReceivedResponse(URLResponsePtr response) MOJO_OVERRIDE {
+ PrintResponse(response);
+ PrintResponseBody();
+ Start();
+ }
+
+ virtual void OnReceivedError(NetworkErrorPtr error) MOJO_OVERRIDE {
+ printf("Got error: %d (%s)\n",
+ error->code, error->description.get().c_str());
+ }
+
+ virtual void OnReceivedEndOfResponseBody() MOJO_OVERRIDE {
+ // Ignored.
+ }
+
+ void Start() {
+ std::string url = PromptForURL();
+ printf("Loading: %s\n", url.c_str());
+
+ network_service_->CreateURLLoader(Get(&url_loader_));
+ url_loader_.set_client(this);
+
+ URLRequestPtr request(URLRequest::New());
+ request->url = url;
+ request->method = "GET";
+ request->follow_redirects = true;
+
+ DataPipe data_pipe;
+ response_body_stream_ = data_pipe.consumer_handle.Pass();
+
+ url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass());
+ }
+
+ std::string PromptForURL() {
+ printf("Enter URL> ");
+ char buf[1024];
+ if (scanf("%1023s", buf) != 1)
+ buf[0] = '\0';
+ return buf;
+ }
+
+ void PrintResponse(const URLResponsePtr& response) {
+ printf(">>> Headers <<< \n");
+ printf(" %s\n", response->status_line.get().c_str());
+ if (response->headers) {
+ for (size_t i = 0; i < response->headers.size(); ++i)
+ printf(" %s\n", response->headers[i].get().c_str());
+ }
+ }
+
+ void PrintResponseBody() {
+ // Read response body in blocking fashion.
+ printf(">>> Body <<<\n");
+
+ for (;;) {
+ char buf[512];
+ uint32_t num_bytes = sizeof(buf);
+ MojoResult result = ReadDataRaw(
+ response_body_stream_.get(),
+ buf,
+ &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ Wait(response_body_stream_.get(),
+ MOJO_WAIT_FLAG_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ } else if (result == MOJO_RESULT_OK) {
+ fwrite(buf, num_bytes, 1, stdout);
+ } else {
+ break;
+ }
+ }
+
+ printf("\n>>> EOF <<<\n");
+ }
+
+ NetworkServicePtr network_service_;
+ URLLoaderPtr url_loader_;
+ ScopedDataPipeConsumerHandle response_body_stream_;
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::WGetApp();
+}
+
+} // namespace mojo
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
index 6fe753b..ea21a85 100644
--- a/mojo/mojo.gyp
+++ b/mojo/mojo.gyp
@@ -39,6 +39,7 @@
'mojo_js_unittests',
'mojo_message_generator',
'mojo_native_viewport_service',
+ 'mojo_network_service',
'mojo_pepper_container_app',
'mojo_public_test_utils',
'mojo_public_bindings_unittests',
@@ -58,6 +59,7 @@
'mojo_utility',
'mojo_view_manager_lib',
'mojo_view_manager_lib_unittests',
+ 'mojo_wget',
],
'conditions': [
['use_aura==1', {
diff --git a/mojo/mojo_examples.gypi b/mojo/mojo_examples.gypi
index a352922..9fe1f1b 100644
--- a/mojo/mojo_examples.gypi
+++ b/mojo/mojo_examples.gypi
@@ -67,6 +67,28 @@
'includes': [ 'build/package_app.gypi' ],
},
{
+ 'target_name': 'mojo_wget',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ 'mojo_environment_standalone',
+ 'mojo_main_standalone',
+ 'mojo_network_bindings',
+ 'mojo_system',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/wget/wget.cc',
+ ],
+ },
+ {
+ 'target_name': 'package_mojo_wget',
+ 'variables': {
+ 'app_name': 'mojo_wget',
+ },
+ 'includes': [ 'build/package_app.gypi' ],
+ },
+ {
'target_name': 'mojo_pepper_container_app',
'type': 'shared_library',
'dependencies': [
diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi
index 2661724..b4b691b 100644
--- a/mojo/mojo_services.gypi
+++ b/mojo/mojo_services.gypi
@@ -183,6 +183,48 @@
],
},
{
+ 'target_name': 'mojo_network_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/network/network_error.mojom',
+ 'services/public/interfaces/network/network_service.mojom',
+ 'services/public/interfaces/network/url_loader.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_network_service',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../net/net.gyp:net',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_network_bindings',
+ 'mojo_system_impl',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_network_bindings',
+ ],
+ 'sources': [
+ 'services/network/main.cc',
+ 'services/network/network_context.cc',
+ 'services/network/network_context.h',
+ 'services/network/network_service_impl.cc',
+ 'services/network/network_service_impl.h',
+ 'services/network/url_loader_impl.cc',
+ 'services/network/url_loader_impl.h',
+ ],
+ },
+ {
'target_name': 'mojo_view_manager_common',
'type': 'static_library',
'sources': [
diff --git a/mojo/public/cpp/application/lib/mojo_main_chromium.cc b/mojo/public/cpp/application/lib/mojo_main_chromium.cc
index 7f9f133..cda7cd0 100644
--- a/mojo/public/cpp/application/lib/mojo_main_chromium.cc
+++ b/mojo/public/cpp/application/lib/mojo_main_chromium.cc
@@ -6,8 +6,6 @@
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "mojo/public/cpp/application/application.h"
-#include "mojo/public/cpp/environment/environment.h"
-#include "mojo/public/cpp/utility/run_loop.h"
extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
MojoHandle service_provider_handle) {
@@ -15,7 +13,7 @@ extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
base::AtExitManager at_exit;
base::MessageLoop loop;
- scoped_ptr<mojo::Application>app(mojo::Application::Create());
+ scoped_ptr<mojo::Application> app(mojo::Application::Create());
app->BindServiceProvider(
mojo::MakeScopedHandle(mojo::MessagePipeHandle(service_provider_handle)));
app->Initialize();
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index a373e9b..3cc8768 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -93,6 +93,7 @@ void Connector::CallOnHandleReady(void* closure, MojoResult result) {
}
void Connector::OnHandleReady(MojoResult result) {
+ assert(async_wait_id_ != 0);
async_wait_id_ = 0;
if (result == MOJO_RESULT_OK) {
diff --git a/mojo/services/network/DEPS b/mojo/services/network/DEPS
new file mode 100644
index 0000000..dc0a052
--- /dev/null
+++ b/mojo/services/network/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/services",
+ "+net",
+]
diff --git a/mojo/services/network/main.cc b/mojo/services/network/main.cc
new file mode 100644
index 0000000..1b69638
--- /dev/null
+++ b/mojo/services/network/main.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/network/network_context.h"
+#include "mojo/services/network/network_service_impl.h"
+
+extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
+ MojoHandle service_provider_handle) {
+ base::CommandLine::Init(0, NULL);
+ base::AtExitManager at_exit;
+
+ // The IO message loop allows us to use net::URLRequest on this thread.
+ base::MessageLoopForIO loop;
+
+ mojo::NetworkContext context;
+
+ mojo::Application app;
+ app.BindServiceProvider(
+ mojo::MakeScopedHandle(mojo::MessagePipeHandle(service_provider_handle)));
+
+ app.AddService<mojo::NetworkServiceImpl>(&context);
+
+ loop.Run();
+ return MOJO_RESULT_OK;
+}
diff --git a/mojo/services/network/network_context.cc b/mojo/services/network/network_context.cc
new file mode 100644
index 0000000..099432a
--- /dev/null
+++ b/mojo/services/network/network_context.cc
@@ -0,0 +1,123 @@
+// Copyright 2014 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 "mojo/services/network/network_context.h"
+
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_server_properties_impl.h"
+#include "net/http/transport_security_persister.h"
+#include "net/http/transport_security_state.h"
+#include "net/proxy/proxy_service.h"
+#include "net/ssl/default_server_bound_cert_store.h"
+#include "net/ssl/server_bound_cert_service.h"
+#include "net/ssl/ssl_config_service_defaults.h"
+#include "net/url_request/file_protocol_handler.h"
+#include "net/url_request/static_http_user_agent_settings.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_storage.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+
+namespace mojo {
+
+NetworkContext::NetworkContext()
+ : file_thread_("network_file_thread"),
+ cache_thread_("network_cache_thread") {
+ file_thread_.Start();
+
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ cache_thread_.StartWithOptions(options);
+
+ // TODO(darin): Need to figure out a better base path, obviously.
+ base::FilePath base_path;
+ PathService::Get(base::DIR_TEMP, &base_path);
+ base_path = base_path.Append(FILE_PATH_LITERAL("network_service"));
+
+ url_request_context_.reset(new net::URLRequestContext());
+ url_request_context_->set_net_log(net_log_.get());
+
+ storage_.reset(
+ new net::URLRequestContextStorage(url_request_context_.get()));
+
+ storage_->set_cookie_store(new net::CookieMonster(NULL, NULL));
+
+ // TODO(darin): This is surely the wrong UA string.
+ storage_->set_http_user_agent_settings(
+ new net::StaticHttpUserAgentSettings("en-us,en", "Mojo/0.1"));
+
+ storage_->set_proxy_service(net::ProxyService::CreateDirect());
+ storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
+ storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
+
+ net::TransportSecurityState* transport_security_state =
+ new net::TransportSecurityState();
+ storage_->set_transport_security_state(transport_security_state);
+
+ transport_security_persister_.reset(
+ new net::TransportSecurityPersister(
+ transport_security_state,
+ base_path,
+ file_thread_.message_loop_proxy(),
+ false));
+
+ storage_->set_server_bound_cert_service(
+ new net::ServerBoundCertService(
+ new net::DefaultServerBoundCertStore(NULL),
+ file_thread_.message_loop_proxy()));
+ storage_->set_http_server_properties(
+ scoped_ptr<net::HttpServerProperties>(
+ new net::HttpServerPropertiesImpl()));
+ storage_->set_host_resolver(net::HostResolver::CreateDefaultResolver(
+ url_request_context_->net_log()));
+
+ net::HttpNetworkSession::Params network_session_params;
+ network_session_params.cert_verifier =
+ url_request_context_->cert_verifier();
+ network_session_params.transport_security_state =
+ url_request_context_->transport_security_state();
+ network_session_params.server_bound_cert_service =
+ url_request_context_->server_bound_cert_service();
+ network_session_params.net_log =
+ url_request_context_->net_log();
+ network_session_params.proxy_service =
+ url_request_context_->proxy_service();
+ network_session_params.ssl_config_service =
+ url_request_context_->ssl_config_service();
+ network_session_params.http_server_properties =
+ url_request_context_->http_server_properties();
+ network_session_params.host_resolver =
+ url_request_context_->host_resolver();
+
+ base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache"));
+
+ net::HttpCache::DefaultBackend* main_backend =
+ new net::HttpCache::DefaultBackend(
+ net::DISK_CACHE,
+ net::CACHE_BACKEND_DEFAULT,
+ cache_path,
+ 0,
+ cache_thread_.message_loop_proxy());
+
+ net::HttpCache* main_cache = new net::HttpCache(
+ network_session_params, main_backend);
+ storage_->set_http_transaction_factory(main_cache);
+
+ scoped_ptr<net::URLRequestJobFactoryImpl> job_factory(
+ new net::URLRequestJobFactoryImpl());
+ job_factory->SetProtocolHandler(
+ "file",
+ new net::FileProtocolHandler(file_thread_.message_loop_proxy()));
+ storage_->set_job_factory(job_factory.release());
+}
+
+NetworkContext::~NetworkContext() {
+ // TODO(darin): Be careful about destruction order of member variables?
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/network_context.h b/mojo/services/network/network_context.h
new file mode 100644
index 0000000..b156c08
--- /dev/null
+++ b/mojo/services/network/network_context.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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 MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+
+namespace net {
+class NetLog;
+class URLRequestContext;
+class URLRequestContextStorage;
+class TransportSecurityPersister;
+}
+
+namespace mojo {
+
+class NetworkContext {
+ public:
+ NetworkContext();
+ ~NetworkContext();
+
+ net::URLRequestContext* url_request_context() {
+ return url_request_context_.get();
+ }
+
+ private:
+ base::Thread file_thread_;
+ base::Thread cache_thread_;
+ scoped_ptr<net::NetLog> net_log_;
+ scoped_ptr<net::URLRequestContextStorage> storage_;
+ scoped_ptr<net::URLRequestContext> url_request_context_;
+ scoped_ptr<net::TransportSecurityPersister> transport_security_persister_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkContext);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
diff --git a/mojo/services/network/network_service_impl.cc b/mojo/services/network/network_service_impl.cc
new file mode 100644
index 0000000..a216738
--- /dev/null
+++ b/mojo/services/network/network_service_impl.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "mojo/services/network/network_service_impl.h"
+
+#include "mojo/services/network/url_loader_impl.h"
+
+namespace mojo {
+
+NetworkServiceImpl::NetworkServiceImpl(NetworkContext* context)
+ : context_(context) {
+}
+
+NetworkServiceImpl::~NetworkServiceImpl() {
+}
+
+void NetworkServiceImpl::CreateURLLoader(
+ InterfaceRequest<URLLoader> loader) {
+ BindToRequest(new URLLoaderImpl(context_), &loader);
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/network_service_impl.h b/mojo/services/network/network_service_impl.h
new file mode 100644
index 0000000..32d1713
--- /dev/null
+++ b/mojo/services/network/network_service_impl.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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 MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+
+namespace mojo {
+class NetworkContext;
+
+class NetworkServiceImpl : public InterfaceImpl<NetworkService> {
+ public:
+ explicit NetworkServiceImpl(NetworkContext* context);
+ virtual ~NetworkServiceImpl();
+
+ // NetworkService methods:
+ virtual void CreateURLLoader(InterfaceRequest<URLLoader> loader) OVERRIDE;
+
+ private:
+ NetworkContext* context_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc
new file mode 100644
index 0000000..00ed9e7
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.cc
@@ -0,0 +1,232 @@
+// Copyright 2014 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 "mojo/services/network/url_loader_impl.h"
+
+#include "mojo/services/network/network_context.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_response_headers.h"
+
+namespace mojo {
+namespace {
+
+const uint32_t kMaxReadSize = 64 * 1024;
+
+// Generates an URLResponsePtr from the response state of a net::URLRequest.
+URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
+ URLResponsePtr response(URLResponse::New());
+ response->url = url_request->url().spec();
+
+ const net::HttpResponseHeaders* headers = url_request->response_headers();
+ if (headers) {
+ response->status_code = headers->response_code();
+ response->status_line = headers->GetStatusLine();
+
+ std::vector<String> header_lines;
+ void* iter = NULL;
+ std::string name, value;
+ while (headers->EnumerateHeaderLines(&iter, &name, &value))
+ header_lines.push_back(name + ": " + value);
+ if (!header_lines.empty())
+ response->headers.Swap(&header_lines);
+ }
+
+ return response.Pass();
+}
+
+} // namespace
+
+// Keeps track of a pending two-phase write on a DataPipeProducerHandle.
+class URLLoaderImpl::PendingWriteToDataPipe :
+ public base::RefCountedThreadSafe<PendingWriteToDataPipe> {
+ public:
+ explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle)
+ : handle_(handle.Pass()),
+ buffer_(NULL) {
+ }
+
+ bool BeginWrite(uint32_t* num_bytes) {
+ MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+ if (*num_bytes > kMaxReadSize)
+ *num_bytes = kMaxReadSize;
+
+ return result == MOJO_RESULT_OK;
+ }
+
+ ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
+ EndWriteDataRaw(handle_.get(), num_bytes);
+ buffer_ = NULL;
+ return handle_.Pass();
+ }
+
+ char* buffer() { return static_cast<char*>(buffer_); }
+
+ private:
+ friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>;
+
+ ~PendingWriteToDataPipe() {
+ if (handle_.is_valid())
+ EndWriteDataRaw(handle_.get(), 0);
+ }
+
+ ScopedDataPipeProducerHandle handle_;
+ void* buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe);
+};
+
+// Takes ownership of a pending two-phase write on a DataPipeProducerHandle,
+// and makes its buffer available as a net::IOBuffer.
+class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer {
+ public:
+ DependentIOBuffer(PendingWriteToDataPipe* pending_write)
+ : net::WrappedIOBuffer(pending_write->buffer()),
+ pending_write_(pending_write) {
+ }
+ private:
+ virtual ~DependentIOBuffer() {}
+ scoped_refptr<PendingWriteToDataPipe> pending_write_;
+};
+
+URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
+ : context_(context),
+ weak_ptr_factory_(this) {
+}
+
+URLLoaderImpl::~URLLoaderImpl() {
+}
+
+void URLLoaderImpl::OnConnectionError() {
+ delete this;
+}
+
+void URLLoaderImpl::Start(URLRequestPtr request,
+ ScopedDataPipeProducerHandle response_body_stream) {
+ // Do not allow starting another request.
+ if (url_request_) {
+ SendError(net::ERR_UNEXPECTED);
+ url_request_.reset();
+ response_body_stream_.reset();
+ return;
+ }
+
+ if (!request) {
+ SendError(net::ERR_INVALID_ARGUMENT);
+ return;
+ }
+
+ response_body_stream_ = response_body_stream.Pass();
+
+ GURL url(request->url);
+ url_request_.reset(
+ new net::URLRequest(url,
+ net::DEFAULT_PRIORITY,
+ this,
+ context_->url_request_context()));
+ url_request_->Start();
+}
+
+void URLLoaderImpl::FollowRedirect() {
+ NOTIMPLEMENTED();
+}
+
+void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
+ const GURL& new_url,
+ bool* defer_redirect) {
+ DCHECK(url_request == url_request_.get());
+ DCHECK(url_request->status().is_success());
+
+ URLResponsePtr response = MakeURLResponse(url_request);
+ std::string redirect_method =
+ net::URLRequest::ComputeMethodForRedirect(url_request->method(),
+ response->status_code);
+ client()->OnReceivedRedirect(
+ response.Pass(), new_url.spec(), redirect_method);
+
+ *defer_redirect = false;
+}
+
+void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
+ DCHECK(url_request == url_request_.get());
+
+ if (!url_request->status().is_success()) {
+ SendError(url_request->status().error());
+ return;
+ }
+
+ client()->OnReceivedResponse(MakeURLResponse(url_request));
+
+ // Start reading...
+ ReadMore();
+}
+
+void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
+ int bytes_read) {
+ if (url_request->status().is_success()) {
+ DidRead(static_cast<uint32_t>(bytes_read), false);
+ } else {
+ pending_write_ = NULL; // This closes the data pipe.
+ // TODO(darin): Perhaps we should communicate this error to our client.
+ }
+}
+
+void URLLoaderImpl::SendError(int error_code) {
+ NetworkErrorPtr error(NetworkError::New());
+ error->code = error_code;
+ error->description = net::ErrorToString(error_code);
+ client()->OnReceivedError(error.Pass());
+}
+
+void URLLoaderImpl::ReadMore() {
+ DCHECK(!pending_write_);
+
+ pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
+
+ uint32_t num_bytes;
+ if (!pending_write_->BeginWrite(&num_bytes))
+ CHECK(false); // Oops!
+ if (num_bytes > static_cast<uint32_t>(std::numeric_limits<int>::max()))
+ CHECK(false); // Oops!
+
+ scoped_refptr<net::IOBuffer> buf = new DependentIOBuffer(pending_write_);
+
+ int bytes_read;
+ url_request_->Read(buf, static_cast<int>(num_bytes), &bytes_read);
+
+ // Drop our reference to the buffer.
+ buf = NULL;
+
+ if (url_request_->status().is_io_pending()) {
+ // Wait for OnReadCompleted.
+ } else if (url_request_->status().is_success() && bytes_read > 0) {
+ DidRead(static_cast<uint32_t>(bytes_read), true);
+ } else {
+ pending_write_->Complete(0);
+ pending_write_ = NULL; // This closes the data pipe.
+ if (bytes_read == 0) {
+ client()->OnReceivedEndOfResponseBody();
+ } else {
+ DCHECK(!url_request_->status().is_success());
+ SendError(url_request_->status().error());
+ }
+ }
+}
+
+void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
+ DCHECK(url_request_->status().is_success());
+
+ response_body_stream_ = pending_write_->Complete(num_bytes);
+ pending_write_ = NULL;
+
+ if (completed_synchronously) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ ReadMore();
+ }
+}
+
+} // namespace mojo
diff --git a/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h
new file mode 100644
index 0000000..786e04c
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.h
@@ -0,0 +1,60 @@
+// Copyright 2014 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 MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+#define MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request.h"
+
+namespace mojo {
+class NetworkContext;
+
+class URLLoaderImpl : public InterfaceImpl<URLLoader>,
+ public net::URLRequest::Delegate {
+ public:
+ explicit URLLoaderImpl(NetworkContext* context);
+ virtual ~URLLoaderImpl();
+
+ private:
+ class PendingWriteToDataPipe;
+ class DependentIOBuffer;
+
+ // InterfaceImpl<> methods:
+ virtual void OnConnectionError() OVERRIDE;
+
+ // URLLoader methods:
+ virtual void Start(
+ URLRequestPtr request,
+ ScopedDataPipeProducerHandle response_body_stream) OVERRIDE;
+ virtual void FollowRedirect() OVERRIDE;
+
+ // net::URLRequest::Delegate methods:
+ virtual void OnReceivedRedirect(net::URLRequest* url_request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE;
+ virtual void OnResponseStarted(net::URLRequest* url_request) OVERRIDE;
+ virtual void OnReadCompleted(net::URLRequest* url_request, int bytes_read)
+ OVERRIDE;
+
+ void SendError(int error);
+ void ReadMore();
+ void DidRead(uint32_t num_bytes, bool completed_synchronously);
+
+ NetworkContext* context_;
+ scoped_ptr<net::URLRequest> url_request_;
+ ScopedDataPipeProducerHandle response_body_stream_;
+ scoped_refptr<PendingWriteToDataPipe> pending_write_;
+
+ base::WeakPtrFactory<URLLoaderImpl> weak_ptr_factory_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
diff --git a/mojo/services/public/interfaces/network/network_error.mojom b/mojo/services/public/interfaces/network/network_error.mojom
new file mode 100644
index 0000000..d345e93
--- /dev/null
+++ b/mojo/services/public/interfaces/network/network_error.mojom
@@ -0,0 +1,12 @@
+// Copyright 2014 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.
+
+module mojo {
+
+struct NetworkError {
+ int32 code;
+ string description;
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/network_service.mojom b/mojo/services/public/interfaces/network/network_service.mojom
new file mode 100644
index 0000000..7d9cd17
--- /dev/null
+++ b/mojo/services/public/interfaces/network/network_service.mojom
@@ -0,0 +1,15 @@
+// Copyright 2014 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.
+
+import "url_loader.mojom"
+
+module mojo {
+
+interface NetworkService {
+ CreateURLLoader(URLLoader& loader);
+
+ // TODO(darin): Add other methods here (e.g., cookies).
+};
+
+}
diff --git a/mojo/services/public/interfaces/network/url_loader.mojom b/mojo/services/public/interfaces/network/url_loader.mojom
new file mode 100644
index 0000000..4d1298e
--- /dev/null
+++ b/mojo/services/public/interfaces/network/url_loader.mojom
@@ -0,0 +1,67 @@
+// Copyright 2014 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.
+
+import "network_error.mojom"
+
+module mojo {
+
+struct URLRequest {
+ string url;
+ string method;
+ string[] headers;
+ handle<data_pipe_consumer> body;
+ int64 body_length; // Set to -1 if length is unknown.
+ bool follow_redirects;
+};
+
+struct URLResponse {
+ string url;
+ string[] redirects; // The sequence of redirected URLs.
+ uint32 status_code;
+ string status_line;
+ string[] headers;
+};
+
+[Client=URLLoaderClient]
+interface URLLoader {
+ // Start loading the given |request|. When available, the response body will
+ // be copied to |response_body_stream|.
+ //
+ // The client's |OnReceivedResponse| method will run when response meta data
+ // becomes available, or if a redirect is encountered and |follow_redirects|
+ // is false, the client's |OnRecievedRedirect| method will called.
+ //
+ // NOTE: You may observe data being pushed to |response_body_stream| before
+ // you receive |OnReceivedResponse|.
+ Start(URLRequest request, handle<data_pipe_producer> response_body_stream);
+
+ // If the request passed to |Start| was configured with |follow_redirects|
+ // set to false, then upon receiving a redirect, |OnReceivedRedirect| will be
+ // called. To follow the indicated redirect, call the |FollowRedirect|
+ // method.
+ FollowRedirect();
+};
+
+interface URLLoaderClient {
+ // This method is called when a redirect is encountered, provided the
+ // request's |follow_redirects| attribute was set to false.
+ OnReceivedRedirect(URLResponse response, string new_url, string new_method);
+
+ // This method is called when response meta data becomes available.
+ OnReceivedResponse(URLResponse response);
+
+ // This method is called when a network level error is encountered. This can
+ // happen before or after OnReceivedResponse, but cannot happen after
+ // OnReceivedEndOfResponseBody.
+ OnReceivedError(NetworkError error);
+
+ // This method is called when the response body has been successfully
+ // downloaded and copied in its entirety to |response_body_stream|.
+ //
+ // NOTE: Because |response_body_stream| is limited in size, this event may be
+ // delayed until |response_body_stream| is consumed.
+ OnReceivedEndOfResponseBody();
+};
+
+}