1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "mojo/services/network/url_loader_impl.h"
7 #include "base/memory/scoped_vector.h"
8 #include "base/message_loop/message_loop.h"
9 #include "mojo/common/common_type_converters.h"
10 #include "mojo/services/network/network_context.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/load_flags.h"
13 #include "net/base/upload_bytes_element_reader.h"
14 #include "net/base/upload_data_stream.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/url_request/redirect_info.h"
21 const uint32_t kMaxReadSize = 64 * 1024;
23 // Generates an URLResponsePtr from the response state of a net::URLRequest.
24 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
25 URLResponsePtr response(URLResponse::New());
26 response->url = String::From(url_request->url());
28 const net::HttpResponseHeaders* headers = url_request->response_headers();
30 response->status_code = headers->response_code();
31 response->status_line = headers->GetStatusLine();
33 std::vector<String> header_lines;
35 std::string name, value;
36 while (headers->EnumerateHeaderLines(&iter, &name, &value))
37 header_lines.push_back(name + ": " + value);
38 if (!header_lines.empty())
39 response->headers.Swap(&header_lines);
42 std::string mime_type;
43 url_request->GetMimeType(&mime_type);
44 response->mime_type = mime_type;
47 url_request->GetCharset(&charset);
48 response->charset = charset;
50 return response.Pass();
53 NetworkErrorPtr MakeNetworkError(int error_code) {
54 NetworkErrorPtr error = NetworkError::New();
55 error->code = error_code;
56 error->description = net::ErrorToString(error_code);
60 // Reads the request body upload data from a DataPipe.
61 class UploadDataPipeElementReader : public net::UploadElementReader {
63 UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
64 : pipe_(pipe.Pass()), num_bytes_(0) {}
65 virtual ~UploadDataPipeElementReader() {}
67 // UploadElementReader overrides:
68 virtual int Init(const net::CompletionCallback& callback) OVERRIDE {
70 ReadDataRaw(pipe_.get(), NULL, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
73 virtual uint64 GetContentLength() const OVERRIDE {
76 virtual uint64 BytesRemaining() const OVERRIDE {
77 return num_bytes_ - offset_;
79 virtual bool IsInMemory() const OVERRIDE {
82 virtual int Read(net::IOBuffer* buf,
84 const net::CompletionCallback& callback) OVERRIDE {
86 std::min(BytesRemaining(), static_cast<uint64>(buf_length));
88 ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
89 MOJO_READ_DATA_FLAG_NONE);
92 offset_ += bytes_read;
97 ScopedDataPipeConsumerHandle pipe_;
101 DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
106 // Keeps track of a pending two-phase write on a DataPipeProducerHandle.
107 class URLLoaderImpl::PendingWriteToDataPipe :
108 public base::RefCountedThreadSafe<PendingWriteToDataPipe> {
110 explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle)
111 : handle_(handle.Pass()),
115 MojoResult BeginWrite(uint32_t* num_bytes) {
116 MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
117 MOJO_WRITE_DATA_FLAG_NONE);
118 if (*num_bytes > kMaxReadSize)
119 *num_bytes = kMaxReadSize;
124 ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
125 EndWriteDataRaw(handle_.get(), num_bytes);
127 return handle_.Pass();
130 char* buffer() { return static_cast<char*>(buffer_); }
133 friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>;
135 ~PendingWriteToDataPipe() {
136 if (handle_.is_valid())
137 EndWriteDataRaw(handle_.get(), 0);
140 ScopedDataPipeProducerHandle handle_;
143 DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe);
146 // Takes ownership of a pending two-phase write on a DataPipeProducerHandle,
147 // and makes its buffer available as a net::IOBuffer.
148 class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer {
150 DependentIOBuffer(PendingWriteToDataPipe* pending_write)
151 : net::WrappedIOBuffer(pending_write->buffer()),
152 pending_write_(pending_write) {
155 virtual ~DependentIOBuffer() {}
156 scoped_refptr<PendingWriteToDataPipe> pending_write_;
159 URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
161 response_body_buffer_size_(0),
162 auto_follow_redirects_(true),
163 weak_ptr_factory_(this) {
166 URLLoaderImpl::~URLLoaderImpl() {
169 void URLLoaderImpl::Start(URLRequestPtr request,
170 const Callback<void(URLResponsePtr)>& callback) {
172 SendError(net::ERR_UNEXPECTED, callback);
177 SendError(net::ERR_INVALID_ARGUMENT, callback);
182 new net::URLRequest(GURL(request->url),
183 net::DEFAULT_PRIORITY,
185 context_->url_request_context()));
186 url_request_->set_method(request->method);
187 if (request->headers) {
188 net::HttpRequestHeaders headers;
189 for (size_t i = 0; i < request->headers.size(); ++i)
190 headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>());
191 url_request_->SetExtraRequestHeaders(headers);
194 ScopedVector<net::UploadElementReader> element_readers;
195 for (size_t i = 0; i < request->body.size(); ++i) {
196 element_readers.push_back(
197 new UploadDataPipeElementReader(request->body[i].Pass()));
199 url_request_->set_upload(make_scoped_ptr(
200 new net::UploadDataStream(element_readers.Pass(), 0)));
202 if (request->bypass_cache)
203 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
205 callback_ = callback;
206 response_body_buffer_size_ = request->response_body_buffer_size;
207 auto_follow_redirects_ = request->auto_follow_redirects;
209 url_request_->Start();
212 void URLLoaderImpl::FollowRedirect(
213 const Callback<void(URLResponsePtr)>& callback) {
215 SendError(net::ERR_UNEXPECTED, callback);
219 if (auto_follow_redirects_) {
220 DLOG(ERROR) << "Spurious call to FollowRedirect";
221 SendError(net::ERR_UNEXPECTED, callback);
225 // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
226 url_request_->FollowDeferredRedirect();
229 void URLLoaderImpl::QueryStatus(
230 const Callback<void(URLLoaderStatusPtr)>& callback) {
231 URLLoaderStatusPtr status(URLLoaderStatus::New());
233 status->is_loading = url_request_->is_pending();
234 if (!url_request_->status().is_success())
235 status->error = MakeNetworkError(url_request_->status().error());
237 status->is_loading = false;
239 // TODO(darin): Populate more status fields.
240 callback.Run(status.Pass());
243 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
244 const net::RedirectInfo& redirect_info,
245 bool* defer_redirect) {
246 DCHECK(url_request == url_request_.get());
247 DCHECK(url_request->status().is_success());
249 if (auto_follow_redirects_)
252 // Send the redirect response to the client, allowing them to inspect it and
253 // optionally follow the redirect.
254 *defer_redirect = true;
256 URLResponsePtr response = MakeURLResponse(url_request);
257 response->redirect_method = redirect_info.new_method;
258 response->redirect_url = String::From(redirect_info.new_url);
260 SendResponse(response.Pass());
263 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
264 DCHECK(url_request == url_request_.get());
266 if (!url_request->status().is_success()) {
267 SendError(url_request->status().error(), callback_);
268 callback_ = Callback<void(URLResponsePtr)>();
272 // TODO(darin): Add support for optional MIME sniffing.
275 // TODO(darin): Honor given buffer size.
277 URLResponsePtr response = MakeURLResponse(url_request);
278 response->body = data_pipe.consumer_handle.Pass();
279 response_body_stream_ = data_pipe.producer_handle.Pass();
281 SendResponse(response.Pass());
287 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
289 DCHECK(url_request == url_request_.get());
291 if (url_request->status().is_success()) {
292 DidRead(static_cast<uint32_t>(bytes_read), false);
294 pending_write_ = NULL; // This closes the data pipe.
298 void URLLoaderImpl::SendError(
300 const Callback<void(URLResponsePtr)>& callback) {
301 URLResponsePtr response(URLResponse::New());
303 response->url = String::From(url_request_->url());
304 response->error = MakeNetworkError(error_code);
305 callback.Run(response.Pass());
308 void URLLoaderImpl::SendResponse(URLResponsePtr response) {
309 Callback<void(URLResponsePtr)> callback;
310 std::swap(callback_, callback);
311 callback.Run(response.Pass());
314 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
315 // TODO(darin): Handle a bad |result| value.
319 void URLLoaderImpl::WaitToReadMore() {
320 handle_watcher_.Start(response_body_stream_.get(),
321 MOJO_HANDLE_SIGNAL_WRITABLE,
322 MOJO_DEADLINE_INDEFINITE,
323 base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
324 weak_ptr_factory_.GetWeakPtr()));
327 void URLLoaderImpl::ReadMore() {
328 DCHECK(!pending_write_);
330 pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
333 MojoResult result = pending_write_->BeginWrite(&num_bytes);
334 if (result == MOJO_RESULT_SHOULD_WAIT) {
335 // The pipe is full. We need to wait for it to have more space.
336 response_body_stream_ = pending_write_->Complete(num_bytes);
337 pending_write_ = NULL;
341 if (result != MOJO_RESULT_OK) {
342 // The response body stream is in a bad state. Bail.
343 // TODO(darin): How should this be communicated to our client?
346 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
348 scoped_refptr<net::IOBuffer> buf = new DependentIOBuffer(pending_write_);
351 url_request_->Read(buf, static_cast<int>(num_bytes), &bytes_read);
353 // Drop our reference to the buffer.
356 if (url_request_->status().is_io_pending()) {
357 // Wait for OnReadCompleted.
358 } else if (url_request_->status().is_success() && bytes_read > 0) {
359 DidRead(static_cast<uint32_t>(bytes_read), true);
361 pending_write_->Complete(0);
362 pending_write_ = NULL; // This closes the data pipe.
366 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
367 DCHECK(url_request_->status().is_success());
369 response_body_stream_ = pending_write_->Complete(num_bytes);
370 pending_write_ = NULL;
372 if (completed_synchronously) {
373 base::MessageLoop::current()->PostTask(
375 base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));