[Android] Introduce new UMA action for when user copies Image URL from context menu
[chromium-blink-merge.git] / mojo / services / network / url_loader_impl.cc
blobe2e88a1b43b1cd1ebbd77fa06747ddd5ec6175f0
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/common/url_type_converters.h"
11 #include "mojo/services/network/net_adapters.h"
12 #include "mojo/services/network/network_context.h"
13 #include "net/base/elements_upload_data_stream.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/upload_bytes_element_reader.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/url_request/redirect_info.h"
19 #include "net/url_request/url_request_context.h"
21 namespace mojo {
22 namespace {
24 // Generates an URLResponsePtr from the response state of a net::URLRequest.
25 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
26 URLResponsePtr response(URLResponse::New());
27 response->url = String::From(url_request->url());
29 const net::HttpResponseHeaders* headers = url_request->response_headers();
30 if (headers) {
31 response->status_code = headers->response_code();
32 response->status_line = headers->GetStatusLine();
34 response->headers = Array<HttpHeaderPtr>::New(0);
35 std::vector<String> header_lines;
36 void* iter = nullptr;
37 std::string name, value;
38 while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
39 HttpHeaderPtr header = HttpHeader::New();
40 header->name = name;
41 header->value = value;
42 response->headers.push_back(header.Pass());
46 std::string mime_type;
47 url_request->GetMimeType(&mime_type);
48 response->mime_type = mime_type;
50 std::string charset;
51 url_request->GetCharset(&charset);
52 response->charset = charset;
54 return response.Pass();
57 // Reads the request body upload data from a DataPipe.
58 class UploadDataPipeElementReader : public net::UploadElementReader {
59 public:
60 UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
61 : pipe_(pipe.Pass()), num_bytes_(0) {}
62 ~UploadDataPipeElementReader() override {}
64 // UploadElementReader overrides:
65 int Init(const net::CompletionCallback& callback) override {
66 offset_ = 0;
67 ReadDataRaw(pipe_.get(), nullptr, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
68 return net::OK;
70 uint64 GetContentLength() const override { return num_bytes_; }
71 uint64 BytesRemaining() const override { return num_bytes_ - offset_; }
72 bool IsInMemory() const override { return false; }
73 int Read(net::IOBuffer* buf,
74 int buf_length,
75 const net::CompletionCallback& callback) override {
76 uint32_t bytes_read =
77 std::min(static_cast<uint32_t>(BytesRemaining()),
78 static_cast<uint32_t>(buf_length));
79 if (bytes_read > 0) {
80 ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
81 MOJO_READ_DATA_FLAG_NONE);
84 offset_ += bytes_read;
85 return bytes_read;
88 private:
89 ScopedDataPipeConsumerHandle pipe_;
90 uint32_t num_bytes_;
91 uint32_t offset_;
93 DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
96 } // namespace
98 URLLoaderImpl::URLLoaderImpl(NetworkContext* context,
99 InterfaceRequest<URLLoader> request,
100 scoped_ptr<mojo::AppRefCount> app_refcount)
101 : context_(context),
102 response_body_buffer_size_(0),
103 auto_follow_redirects_(true),
104 connected_(true),
105 binding_(this, request.Pass()),
106 app_refcount_(app_refcount.Pass()),
107 weak_ptr_factory_(this) {
108 binding_.set_error_handler(this);
109 context_->RegisterURLLoader(this);
112 URLLoaderImpl::~URLLoaderImpl() {
113 context_->DeregisterURLLoader(this);
116 void URLLoaderImpl::Cleanup() {
117 // The associated network context is going away and we have to destroy
118 // net::URLRequest hold by this loader.
119 delete this;
122 void URLLoaderImpl::Start(URLRequestPtr request,
123 const Callback<void(URLResponsePtr)>& callback) {
124 if (url_request_) {
125 SendError(net::ERR_UNEXPECTED, callback);
126 return;
129 if (!request) {
130 SendError(net::ERR_INVALID_ARGUMENT, callback);
131 return;
134 url_request_ = context_->url_request_context()->CreateRequest(
135 GURL(request->url), net::DEFAULT_PRIORITY, this);
136 url_request_->set_method(request->method);
137 // TODO(jam): need to specify this policy.
138 url_request_->set_referrer_policy(
139 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
140 if (request->headers) {
141 net::HttpRequestHeaders headers;
142 for (size_t i = 0; i < request->headers.size(); ++i) {
143 base::StringPiece header =
144 request->headers[i]->name.To<base::StringPiece>();
145 base::StringPiece value =
146 request->headers[i]->value.To<base::StringPiece>();
147 if (header == net::HttpRequestHeaders::kReferer) {
148 url_request_->SetReferrer(value.as_string());
149 } else {
150 headers.SetHeader(header, value);
153 url_request_->SetExtraRequestHeaders(headers);
155 if (request->body) {
156 ScopedVector<net::UploadElementReader> element_readers;
157 for (size_t i = 0; i < request->body.size(); ++i) {
158 element_readers.push_back(
159 new UploadDataPipeElementReader(request->body[i].Pass()));
161 url_request_->set_upload(make_scoped_ptr<net::UploadDataStream>(
162 new net::ElementsUploadDataStream(element_readers.Pass(), 0)));
164 if (request->bypass_cache)
165 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
167 callback_ = callback;
168 response_body_buffer_size_ = request->response_body_buffer_size;
169 auto_follow_redirects_ = request->auto_follow_redirects;
171 url_request_->Start();
174 void URLLoaderImpl::FollowRedirect(
175 const Callback<void(URLResponsePtr)>& callback) {
176 if (!url_request_) {
177 SendError(net::ERR_UNEXPECTED, callback);
178 return;
181 if (auto_follow_redirects_) {
182 DLOG(ERROR) << "Spurious call to FollowRedirect";
183 SendError(net::ERR_UNEXPECTED, callback);
184 return;
187 // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
188 url_request_->FollowDeferredRedirect();
189 callback_ = callback;
192 void URLLoaderImpl::QueryStatus(
193 const Callback<void(URLLoaderStatusPtr)>& callback) {
194 URLLoaderStatusPtr status(URLLoaderStatus::New());
195 if (url_request_) {
196 status->is_loading = url_request_->is_pending();
197 if (!url_request_->status().is_success())
198 status->error = MakeNetworkError(url_request_->status().error());
199 } else {
200 status->is_loading = false;
202 // TODO(darin): Populate more status fields.
203 callback.Run(status.Pass());
206 void URLLoaderImpl::OnConnectionError() {
207 connected_ = false;
208 DeleteIfNeeded();
211 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
212 const net::RedirectInfo& redirect_info,
213 bool* defer_redirect) {
214 DCHECK(url_request == url_request_.get());
215 DCHECK(url_request->status().is_success());
217 if (auto_follow_redirects_)
218 return;
220 // Send the redirect response to the client, allowing them to inspect it and
221 // optionally follow the redirect.
222 *defer_redirect = true;
224 URLResponsePtr response = MakeURLResponse(url_request);
225 response->redirect_method = redirect_info.new_method;
226 response->redirect_url = String::From(redirect_info.new_url);
227 response->redirect_referrer = redirect_info.new_referrer;
229 SendResponse(response.Pass());
231 DeleteIfNeeded();
234 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
235 DCHECK(url_request == url_request_.get());
237 if (!url_request->status().is_success()) {
238 SendError(url_request->status().error(), callback_);
239 callback_ = Callback<void(URLResponsePtr)>();
240 DeleteIfNeeded();
241 return;
244 // TODO(darin): Add support for optional MIME sniffing.
246 DataPipe data_pipe;
247 // TODO(darin): Honor given buffer size.
249 URLResponsePtr response = MakeURLResponse(url_request);
250 response->body = data_pipe.consumer_handle.Pass();
251 response_body_stream_ = data_pipe.producer_handle.Pass();
252 ListenForPeerClosed();
254 SendResponse(response.Pass());
256 // Start reading...
257 ReadMore();
260 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
261 int bytes_read) {
262 DCHECK(url_request == url_request_.get());
264 if (url_request->status().is_success()) {
265 DidRead(static_cast<uint32_t>(bytes_read), false);
266 } else {
267 handle_watcher_.Stop();
268 pending_write_ = nullptr; // This closes the data pipe.
269 DeleteIfNeeded();
270 return;
274 void URLLoaderImpl::SendError(
275 int error_code,
276 const Callback<void(URLResponsePtr)>& callback) {
277 URLResponsePtr response(URLResponse::New());
278 if (url_request_)
279 response->url = String::From(url_request_->url());
280 response->error = MakeNetworkError(error_code);
281 callback.Run(response.Pass());
284 void URLLoaderImpl::SendResponse(URLResponsePtr response) {
285 Callback<void(URLResponsePtr)> callback;
286 std::swap(callback_, callback);
287 callback.Run(response.Pass());
290 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
291 // TODO(darin): Handle a bad |result| value.
293 // Continue watching the handle in case the peer is closed.
294 ListenForPeerClosed();
295 ReadMore();
298 void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) {
299 url_request_.reset();
300 response_body_stream_.reset();
301 pending_write_ = nullptr;
302 DeleteIfNeeded();
305 void URLLoaderImpl::ReadMore() {
306 DCHECK(!pending_write_.get());
308 uint32_t num_bytes;
309 MojoResult result = NetToMojoPendingBuffer::BeginWrite(
310 &response_body_stream_, &pending_write_, &num_bytes);
312 if (result == MOJO_RESULT_SHOULD_WAIT) {
313 // The pipe is full. We need to wait for it to have more space.
314 handle_watcher_.Start(response_body_stream_.get(),
315 MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE,
316 base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
317 base::Unretained(this)));
318 return;
319 } else if (result != MOJO_RESULT_OK) {
320 // The response body stream is in a bad state. Bail.
321 // TODO(darin): How should this be communicated to our client?
322 handle_watcher_.Stop();
323 response_body_stream_.reset();
324 DeleteIfNeeded();
325 return;
327 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
329 scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(pending_write_.get()));
331 int bytes_read;
332 url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read);
333 if (url_request_->status().is_io_pending()) {
334 // Wait for OnReadCompleted.
335 } else if (url_request_->status().is_success() && bytes_read > 0) {
336 DidRead(static_cast<uint32_t>(bytes_read), true);
337 } else {
338 handle_watcher_.Stop();
339 pending_write_->Complete(0);
340 pending_write_ = nullptr; // This closes the data pipe.
341 DeleteIfNeeded();
342 return;
346 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
347 DCHECK(url_request_->status().is_success());
349 response_body_stream_ = pending_write_->Complete(num_bytes);
350 pending_write_ = nullptr;
352 if (completed_synchronously) {
353 base::MessageLoop::current()->PostTask(
354 FROM_HERE,
355 base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
356 } else {
357 ReadMore();
361 void URLLoaderImpl::DeleteIfNeeded() {
362 bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid();
363 if (!connected_ && !has_data_pipe)
364 delete this;
367 void URLLoaderImpl::ListenForPeerClosed() {
368 handle_watcher_.Start(response_body_stream_.get(),
369 MOJO_HANDLE_SIGNAL_PEER_CLOSED,
370 MOJO_DEADLINE_INDEFINITE,
371 base::Bind(&URLLoaderImpl::OnResponseBodyStreamClosed,
372 base::Unretained(this)));
375 } // namespace mojo