cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / mojo / services / network / url_loader_impl.cc
blob002f2ee45ab7b34b5818744569edf17ce8ec1d13
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/registry_controlled_domains/registry_controlled_domain.h"
17 #include "net/base/upload_bytes_element_reader.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/url_request/redirect_info.h"
20 #include "net/url_request/url_request_context.h"
22 namespace mojo {
23 namespace {
25 GURL GetSiteForURL(const GURL& url) {
26 // If the url has a host, then determine the site.
27 if (url.has_host()) {
28 // Only keep the scheme and registered domain as given by GetOrigin. This
29 // may also include a port, which we need to drop.
30 GURL site = url.GetOrigin();
32 // Remove port, if any.
33 if (site.has_port()) {
34 GURL::Replacements rep;
35 rep.ClearPort();
36 site = site.ReplaceComponents(rep);
39 // If this URL has a registered domain, we only want to remember that part.
40 std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
41 url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
42 if (!domain.empty()) {
43 GURL::Replacements rep;
44 rep.SetHostStr(domain);
45 site = site.ReplaceComponents(rep);
47 return site;
50 // If there is no host but there is a scheme, return the scheme.
51 // This is useful for cases like file URLs.
52 if (url.has_scheme())
53 return GURL(url.scheme() + ":");
55 // Otherwise the URL should be invalid; return an empty site.
56 DCHECK(!url.is_valid());
57 return GURL();
60 // Generates an URLResponsePtr from the response state of a net::URLRequest.
61 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
62 URLResponsePtr response(URLResponse::New());
63 response->url = String::From(url_request->url());
64 response->site = GetSiteForURL(url_request->url()).spec();
66 const net::HttpResponseHeaders* headers = url_request->response_headers();
67 if (headers) {
68 response->status_code = headers->response_code();
69 response->status_line = headers->GetStatusLine();
71 response->headers = Array<HttpHeaderPtr>::New(0);
72 std::vector<String> header_lines;
73 void* iter = nullptr;
74 std::string name, value;
75 while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
76 HttpHeaderPtr header = HttpHeader::New();
77 header->name = name;
78 header->value = value;
79 response->headers.push_back(header.Pass());
83 std::string mime_type;
84 url_request->GetMimeType(&mime_type);
85 response->mime_type = mime_type;
87 std::string charset;
88 url_request->GetCharset(&charset);
89 response->charset = charset;
91 return response.Pass();
94 // Reads the request body upload data from a DataPipe.
95 class UploadDataPipeElementReader : public net::UploadElementReader {
96 public:
97 UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe)
98 : pipe_(pipe.Pass()), num_bytes_(0) {}
99 ~UploadDataPipeElementReader() override {}
101 // UploadElementReader overrides:
102 int Init(const net::CompletionCallback& callback) override {
103 offset_ = 0;
104 ReadDataRaw(pipe_.get(), nullptr, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY);
105 return net::OK;
107 uint64 GetContentLength() const override { return num_bytes_; }
108 uint64 BytesRemaining() const override { return num_bytes_ - offset_; }
109 bool IsInMemory() const override { return false; }
110 int Read(net::IOBuffer* buf,
111 int buf_length,
112 const net::CompletionCallback& callback) override {
113 uint32_t bytes_read =
114 std::min(static_cast<uint32_t>(BytesRemaining()),
115 static_cast<uint32_t>(buf_length));
116 if (bytes_read > 0) {
117 ReadDataRaw(pipe_.get(), buf->data(), &bytes_read,
118 MOJO_READ_DATA_FLAG_NONE);
121 offset_ += bytes_read;
122 return bytes_read;
125 private:
126 ScopedDataPipeConsumerHandle pipe_;
127 uint32_t num_bytes_;
128 uint32_t offset_;
130 DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader);
133 } // namespace
135 URLLoaderImpl::URLLoaderImpl(NetworkContext* context,
136 InterfaceRequest<URLLoader> request,
137 scoped_ptr<mojo::AppRefCount> app_refcount)
138 : context_(context),
139 response_body_buffer_size_(0),
140 auto_follow_redirects_(true),
141 connected_(true),
142 binding_(this, request.Pass()),
143 app_refcount_(app_refcount.Pass()),
144 weak_ptr_factory_(this) {
145 binding_.set_connection_error_handler([this]() { OnConnectionError(); });
146 context_->RegisterURLLoader(this);
149 URLLoaderImpl::~URLLoaderImpl() {
150 context_->DeregisterURLLoader(this);
153 void URLLoaderImpl::Cleanup() {
154 // The associated network context is going away and we have to destroy
155 // net::URLRequest hold by this loader.
156 delete this;
159 void URLLoaderImpl::Start(URLRequestPtr request,
160 const Callback<void(URLResponsePtr)>& callback) {
161 if (url_request_) {
162 SendError(net::ERR_UNEXPECTED, callback);
163 return;
166 if (!request) {
167 SendError(net::ERR_INVALID_ARGUMENT, callback);
168 return;
171 url_request_ = context_->url_request_context()->CreateRequest(
172 GURL(request->url), net::DEFAULT_PRIORITY, this);
173 url_request_->set_method(request->method);
174 // TODO(jam): need to specify this policy.
175 url_request_->set_referrer_policy(
176 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
177 if (request->headers) {
178 net::HttpRequestHeaders headers;
179 for (size_t i = 0; i < request->headers.size(); ++i) {
180 base::StringPiece header =
181 request->headers[i]->name.To<base::StringPiece>();
182 base::StringPiece value =
183 request->headers[i]->value.To<base::StringPiece>();
184 if (header == net::HttpRequestHeaders::kReferer) {
185 url_request_->SetReferrer(value.as_string());
186 } else {
187 headers.SetHeader(header, value);
190 url_request_->SetExtraRequestHeaders(headers);
192 if (request->body) {
193 ScopedVector<net::UploadElementReader> element_readers;
194 for (size_t i = 0; i < request->body.size(); ++i) {
195 element_readers.push_back(
196 new UploadDataPipeElementReader(request->body[i].Pass()));
198 url_request_->set_upload(make_scoped_ptr<net::UploadDataStream>(
199 new net::ElementsUploadDataStream(element_readers.Pass(), 0)));
201 if (request->bypass_cache)
202 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
204 callback_ = callback;
205 response_body_buffer_size_ = request->response_body_buffer_size;
206 auto_follow_redirects_ = request->auto_follow_redirects;
208 url_request_->Start();
211 void URLLoaderImpl::FollowRedirect(
212 const Callback<void(URLResponsePtr)>& callback) {
213 if (!url_request_) {
214 SendError(net::ERR_UNEXPECTED, callback);
215 return;
218 if (auto_follow_redirects_) {
219 DLOG(ERROR) << "Spurious call to FollowRedirect";
220 SendError(net::ERR_UNEXPECTED, callback);
221 return;
224 // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect.
225 url_request_->FollowDeferredRedirect();
226 callback_ = callback;
229 void URLLoaderImpl::QueryStatus(
230 const Callback<void(URLLoaderStatusPtr)>& callback) {
231 URLLoaderStatusPtr status(URLLoaderStatus::New());
232 if (url_request_) {
233 status->is_loading = url_request_->is_pending();
234 if (!url_request_->status().is_success())
235 status->error = MakeNetworkError(url_request_->status().error());
236 } else {
237 status->is_loading = false;
239 // TODO(darin): Populate more status fields.
240 callback.Run(status.Pass());
243 void URLLoaderImpl::OnConnectionError() {
244 connected_ = false;
245 DeleteIfNeeded();
248 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
249 const net::RedirectInfo& redirect_info,
250 bool* defer_redirect) {
251 DCHECK(url_request == url_request_.get());
252 DCHECK(url_request->status().is_success());
254 if (auto_follow_redirects_)
255 return;
257 // Send the redirect response to the client, allowing them to inspect it and
258 // optionally follow the redirect.
259 *defer_redirect = true;
261 URLResponsePtr response = MakeURLResponse(url_request);
262 response->redirect_method = redirect_info.new_method;
263 response->redirect_url = String::From(redirect_info.new_url);
264 response->redirect_referrer = redirect_info.new_referrer;
266 SendResponse(response.Pass());
268 DeleteIfNeeded();
271 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
272 DCHECK(url_request == url_request_.get());
274 if (!url_request->status().is_success()) {
275 SendError(url_request->status().error(), callback_);
276 callback_ = Callback<void(URLResponsePtr)>();
277 DeleteIfNeeded();
278 return;
281 // TODO(darin): Add support for optional MIME sniffing.
283 DataPipe data_pipe;
284 // TODO(darin): Honor given buffer size.
286 URLResponsePtr response = MakeURLResponse(url_request);
287 response->body = data_pipe.consumer_handle.Pass();
288 response_body_stream_ = data_pipe.producer_handle.Pass();
289 ListenForPeerClosed();
291 SendResponse(response.Pass());
293 // Start reading...
294 ReadMore();
297 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
298 int bytes_read) {
299 DCHECK(url_request == url_request_.get());
301 if (url_request->status().is_success()) {
302 DidRead(static_cast<uint32_t>(bytes_read), false);
303 } else {
304 handle_watcher_.Stop();
305 pending_write_ = nullptr; // This closes the data pipe.
306 DeleteIfNeeded();
307 return;
311 void URLLoaderImpl::SendError(
312 int error_code,
313 const Callback<void(URLResponsePtr)>& callback) {
314 URLResponsePtr response(URLResponse::New());
315 if (url_request_)
316 response->url = String::From(url_request_->url());
317 response->error = MakeNetworkError(error_code);
318 callback.Run(response.Pass());
321 void URLLoaderImpl::SendResponse(URLResponsePtr response) {
322 Callback<void(URLResponsePtr)> callback;
323 std::swap(callback_, callback);
324 callback.Run(response.Pass());
327 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
328 // TODO(darin): Handle a bad |result| value.
330 // Continue watching the handle in case the peer is closed.
331 ListenForPeerClosed();
332 ReadMore();
335 void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) {
336 url_request_.reset();
337 response_body_stream_.reset();
338 pending_write_ = nullptr;
339 DeleteIfNeeded();
342 void URLLoaderImpl::ReadMore() {
343 DCHECK(!pending_write_.get());
345 uint32_t num_bytes;
346 MojoResult result = NetToMojoPendingBuffer::BeginWrite(
347 &response_body_stream_, &pending_write_, &num_bytes);
349 if (result == MOJO_RESULT_SHOULD_WAIT) {
350 // The pipe is full. We need to wait for it to have more space.
351 handle_watcher_.Start(response_body_stream_.get(),
352 MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE,
353 base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
354 base::Unretained(this)));
355 return;
356 } else if (result != MOJO_RESULT_OK) {
357 // The response body stream is in a bad state. Bail.
358 // TODO(darin): How should this be communicated to our client?
359 handle_watcher_.Stop();
360 response_body_stream_.reset();
361 DeleteIfNeeded();
362 return;
364 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
366 scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(pending_write_.get()));
368 int bytes_read;
369 url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read);
370 if (url_request_->status().is_io_pending()) {
371 // Wait for OnReadCompleted.
372 } else if (url_request_->status().is_success() && bytes_read > 0) {
373 DidRead(static_cast<uint32_t>(bytes_read), true);
374 } else {
375 handle_watcher_.Stop();
376 pending_write_->Complete(0);
377 pending_write_ = nullptr; // This closes the data pipe.
378 DeleteIfNeeded();
379 return;
383 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
384 DCHECK(url_request_->status().is_success());
386 response_body_stream_ = pending_write_->Complete(num_bytes);
387 pending_write_ = nullptr;
389 if (completed_synchronously) {
390 base::MessageLoop::current()->PostTask(
391 FROM_HERE,
392 base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
393 } else {
394 ReadMore();
398 void URLLoaderImpl::DeleteIfNeeded() {
399 bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid();
400 if (!connected_ && !has_data_pipe)
401 delete this;
404 void URLLoaderImpl::ListenForPeerClosed() {
405 handle_watcher_.Start(response_body_stream_.get(),
406 MOJO_HANDLE_SIGNAL_PEER_CLOSED,
407 MOJO_DEADLINE_INDEFINITE,
408 base::Bind(&URLLoaderImpl::OnResponseBodyStreamClosed,
409 base::Unretained(this)));
412 } // namespace mojo