diff options
author | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-05 09:27:30 +0000 |
---|---|---|
committer | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-05 09:27:30 +0000 |
commit | dd6ee450f95406f6d033dbe716229078adfc000b (patch) | |
tree | 4151d9595c4d57e9569f1d97090533dfdc101594 | |
parent | 2d6251ff1c9ce19585c57e2ee693e3f035ada062 (diff) | |
download | chromium_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.cc | 116 | ||||
-rw-r--r-- | mojo/mojo.gyp | 2 | ||||
-rw-r--r-- | mojo/mojo_examples.gypi | 22 | ||||
-rw-r--r-- | mojo/mojo_services.gypi | 42 | ||||
-rw-r--r-- | mojo/public/cpp/application/lib/mojo_main_chromium.cc | 4 | ||||
-rw-r--r-- | mojo/public/cpp/bindings/lib/connector.cc | 1 | ||||
-rw-r--r-- | mojo/services/network/DEPS | 4 | ||||
-rw-r--r-- | mojo/services/network/main.cc | 30 | ||||
-rw-r--r-- | mojo/services/network/network_context.cc | 123 | ||||
-rw-r--r-- | mojo/services/network/network_context.h | 42 | ||||
-rw-r--r-- | mojo/services/network/network_service_impl.cc | 23 | ||||
-rw-r--r-- | mojo/services/network/network_service_impl.h | 29 | ||||
-rw-r--r-- | mojo/services/network/url_loader_impl.cc | 232 | ||||
-rw-r--r-- | mojo/services/network/url_loader_impl.h | 60 | ||||
-rw-r--r-- | mojo/services/public/interfaces/network/network_error.mojom | 12 | ||||
-rw-r--r-- | mojo/services/public/interfaces/network/network_service.mojom | 15 | ||||
-rw-r--r-- | mojo/services/public/interfaces/network/url_loader.mojom | 67 |
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(); +}; + +} |