| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "mojo/services/network/url_loader_impl.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <utility> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/macros.h" | |
| 14 #include "base/memory/scoped_ptr.h" | |
| 15 #include "base/message_loop/message_loop.h" | |
| 16 #include "mojo/common/common_type_converters.h" | |
| 17 #include "mojo/common/url_type_converters.h" | |
| 18 #include "mojo/services/network/net_adapters.h" | |
| 19 #include "mojo/services/network/network_context.h" | |
| 20 #include "net/base/elements_upload_data_stream.h" | |
| 21 #include "net/base/io_buffer.h" | |
| 22 #include "net/base/load_flags.h" | |
| 23 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
| 24 #include "net/base/upload_bytes_element_reader.h" | |
| 25 #include "net/http/http_response_headers.h" | |
| 26 #include "net/url_request/redirect_info.h" | |
| 27 #include "net/url_request/url_request_context.h" | |
| 28 | |
| 29 namespace mojo { | |
| 30 namespace { | |
| 31 | |
| 32 GURL GetSiteForURL(const GURL& url) { | |
| 33 // If the url has a host, then determine the site. | |
| 34 if (url.has_host()) { | |
| 35 // Only keep the scheme and registered domain as given by GetOrigin. This | |
| 36 // may also include a port, which we need to drop. | |
| 37 GURL site = url.GetOrigin(); | |
| 38 | |
| 39 // Remove port, if any. | |
| 40 if (site.has_port()) { | |
| 41 GURL::Replacements rep; | |
| 42 rep.ClearPort(); | |
| 43 site = site.ReplaceComponents(rep); | |
| 44 } | |
| 45 | |
| 46 // If this URL has a registered domain, we only want to remember that part. | |
| 47 std::string domain = net::registry_controlled_domains::GetDomainAndRegistry( | |
| 48 url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | |
| 49 if (!domain.empty()) { | |
| 50 GURL::Replacements rep; | |
| 51 rep.SetHostStr(domain); | |
| 52 site = site.ReplaceComponents(rep); | |
| 53 } | |
| 54 return site; | |
| 55 } | |
| 56 | |
| 57 // If there is no host but there is a scheme, return the scheme. | |
| 58 // This is useful for cases like file URLs. | |
| 59 if (url.has_scheme()) | |
| 60 return GURL(url.scheme() + ":"); | |
| 61 | |
| 62 // Otherwise the URL should be invalid; return an empty site. | |
| 63 DCHECK(!url.is_valid()); | |
| 64 return GURL(); | |
| 65 } | |
| 66 | |
| 67 // Generates an URLResponsePtr from the response state of a net::URLRequest. | |
| 68 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) { | |
| 69 URLResponsePtr response(URLResponse::New()); | |
| 70 response->url = String::From(url_request->url()); | |
| 71 response->site = GetSiteForURL(url_request->url()).spec(); | |
| 72 | |
| 73 const net::HttpResponseHeaders* headers = url_request->response_headers(); | |
| 74 if (headers) { | |
| 75 response->status_code = headers->response_code(); | |
| 76 response->status_line = headers->GetStatusLine(); | |
| 77 | |
| 78 response->headers = Array<HttpHeaderPtr>::New(0); | |
| 79 std::vector<String> header_lines; | |
| 80 size_t iter = 0; | |
| 81 std::string name, value; | |
| 82 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 83 HttpHeaderPtr header = HttpHeader::New(); | |
| 84 header->name = name; | |
| 85 header->value = value; | |
| 86 response->headers.push_back(std::move(header)); | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 std::string mime_type; | |
| 91 url_request->GetMimeType(&mime_type); | |
| 92 response->mime_type = mime_type; | |
| 93 | |
| 94 std::string charset; | |
| 95 url_request->GetCharset(&charset); | |
| 96 response->charset = charset; | |
| 97 | |
| 98 return response; | |
| 99 } | |
| 100 | |
| 101 // Reads the request body upload data from a DataPipe. | |
| 102 class UploadDataPipeElementReader : public net::UploadElementReader { | |
| 103 public: | |
| 104 UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe) | |
| 105 : pipe_(std::move(pipe)), num_bytes_(0) {} | |
| 106 ~UploadDataPipeElementReader() override {} | |
| 107 | |
| 108 // UploadElementReader overrides: | |
| 109 int Init(const net::CompletionCallback& callback) override { | |
| 110 offset_ = 0; | |
| 111 ReadDataRaw(pipe_.get(), nullptr, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY); | |
| 112 return net::OK; | |
| 113 } | |
| 114 uint64_t GetContentLength() const override { return num_bytes_; } | |
| 115 uint64_t BytesRemaining() const override { return num_bytes_ - offset_; } | |
| 116 bool IsInMemory() const override { return false; } | |
| 117 int Read(net::IOBuffer* buf, | |
| 118 int buf_length, | |
| 119 const net::CompletionCallback& callback) override { | |
| 120 uint32_t bytes_read = | |
| 121 std::min(static_cast<uint32_t>(BytesRemaining()), | |
| 122 static_cast<uint32_t>(buf_length)); | |
| 123 if (bytes_read > 0) { | |
| 124 ReadDataRaw(pipe_.get(), buf->data(), &bytes_read, | |
| 125 MOJO_READ_DATA_FLAG_NONE); | |
| 126 } | |
| 127 | |
| 128 offset_ += bytes_read; | |
| 129 return bytes_read; | |
| 130 } | |
| 131 | |
| 132 private: | |
| 133 ScopedDataPipeConsumerHandle pipe_; | |
| 134 uint32_t num_bytes_; | |
| 135 uint32_t offset_; | |
| 136 | |
| 137 DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader); | |
| 138 }; | |
| 139 | |
| 140 } // namespace | |
| 141 | |
| 142 URLLoaderImpl::URLLoaderImpl(NetworkContext* context, | |
| 143 InterfaceRequest<URLLoader> request, | |
| 144 scoped_ptr<mojo::MessageLoopRef> app_refcount) | |
| 145 : context_(context), | |
| 146 response_body_buffer_size_(0), | |
| 147 response_body_bytes_read_(0), | |
| 148 auto_follow_redirects_(true), | |
| 149 connected_(true), | |
| 150 binding_(this, std::move(request)), | |
| 151 app_refcount_(std::move(app_refcount)), | |
| 152 weak_ptr_factory_(this) { | |
| 153 binding_.set_connection_error_handler([this]() { OnConnectionError(); }); | |
| 154 context_->RegisterURLLoader(this); | |
| 155 } | |
| 156 | |
| 157 URLLoaderImpl::~URLLoaderImpl() { | |
| 158 context_->DeregisterURLLoader(this); | |
| 159 } | |
| 160 | |
| 161 void URLLoaderImpl::Cleanup() { | |
| 162 // The associated network context is going away and we have to destroy | |
| 163 // net::URLRequest hold by this loader. | |
| 164 delete this; | |
| 165 } | |
| 166 | |
| 167 void URLLoaderImpl::Start(URLRequestPtr request, | |
| 168 const Callback<void(URLResponsePtr)>& callback) { | |
| 169 if (url_request_) { | |
| 170 SendError(net::ERR_UNEXPECTED, callback); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 if (!request) { | |
| 175 SendError(net::ERR_INVALID_ARGUMENT, callback); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 url_request_ = context_->url_request_context()->CreateRequest( | |
| 180 GURL(request->url.get()), net::DEFAULT_PRIORITY, this); | |
| 181 url_request_->set_method(request->method); | |
| 182 // TODO(jam): need to specify this policy. | |
| 183 url_request_->set_referrer_policy( | |
| 184 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE); | |
| 185 if (request->headers) { | |
| 186 net::HttpRequestHeaders headers; | |
| 187 for (size_t i = 0; i < request->headers.size(); ++i) { | |
| 188 base::StringPiece header = | |
| 189 request->headers[i]->name.To<base::StringPiece>(); | |
| 190 base::StringPiece value = | |
| 191 request->headers[i]->value.To<base::StringPiece>(); | |
| 192 if (header == net::HttpRequestHeaders::kReferer) { | |
| 193 url_request_->SetReferrer(value.as_string()); | |
| 194 } else { | |
| 195 headers.SetHeader(header, value); | |
| 196 } | |
| 197 } | |
| 198 url_request_->SetExtraRequestHeaders(headers); | |
| 199 } | |
| 200 if (request->body) { | |
| 201 std::vector<scoped_ptr<net::UploadElementReader>> element_readers; | |
| 202 for (size_t i = 0; i < request->body.size(); ++i) { | |
| 203 element_readers.push_back(make_scoped_ptr( | |
| 204 new UploadDataPipeElementReader(std::move(request->body[i])))); | |
| 205 } | |
| 206 url_request_->set_upload(make_scoped_ptr<net::UploadDataStream>( | |
| 207 new net::ElementsUploadDataStream(std::move(element_readers), 0))); | |
| 208 } | |
| 209 if (request->bypass_cache) | |
| 210 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE); | |
| 211 | |
| 212 callback_ = callback; | |
| 213 response_body_buffer_size_ = request->response_body_buffer_size; | |
| 214 auto_follow_redirects_ = request->auto_follow_redirects; | |
| 215 | |
| 216 url_request_->Start(); | |
| 217 } | |
| 218 | |
| 219 void URLLoaderImpl::FollowRedirect( | |
| 220 const Callback<void(URLResponsePtr)>& callback) { | |
| 221 if (!url_request_) { | |
| 222 SendError(net::ERR_UNEXPECTED, callback); | |
| 223 return; | |
| 224 } | |
| 225 | |
| 226 if (auto_follow_redirects_) { | |
| 227 DLOG(ERROR) << "Spurious call to FollowRedirect"; | |
| 228 SendError(net::ERR_UNEXPECTED, callback); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect. | |
| 233 url_request_->FollowDeferredRedirect(); | |
| 234 callback_ = callback; | |
| 235 } | |
| 236 | |
| 237 void URLLoaderImpl::QueryStatus( | |
| 238 const Callback<void(URLLoaderStatusPtr)>& callback) { | |
| 239 URLLoaderStatusPtr status(URLLoaderStatus::New()); | |
| 240 status->bytes_read = response_body_bytes_read_; | |
| 241 if (url_request_) { | |
| 242 status->is_loading = url_request_->is_pending(); | |
| 243 if (!url_request_->status().is_success()) | |
| 244 status->error = MakeNetworkError(url_request_->status().error()); | |
| 245 if (url_request_->response_info().headers) { | |
| 246 status->content_length = | |
| 247 url_request_->response_info().headers->GetContentLength(); | |
| 248 } | |
| 249 } else { | |
| 250 status->is_loading = false; | |
| 251 } | |
| 252 // TODO(darin): Populate more status fields. | |
| 253 callback.Run(std::move(status)); | |
| 254 } | |
| 255 | |
| 256 void URLLoaderImpl::OnConnectionError() { | |
| 257 connected_ = false; | |
| 258 DeleteIfNeeded(); | |
| 259 } | |
| 260 | |
| 261 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request, | |
| 262 const net::RedirectInfo& redirect_info, | |
| 263 bool* defer_redirect) { | |
| 264 DCHECK(url_request == url_request_.get()); | |
| 265 DCHECK(url_request->status().is_success()); | |
| 266 | |
| 267 if (auto_follow_redirects_) | |
| 268 return; | |
| 269 | |
| 270 // Send the redirect response to the client, allowing them to inspect it and | |
| 271 // optionally follow the redirect. | |
| 272 *defer_redirect = true; | |
| 273 | |
| 274 URLResponsePtr response = MakeURLResponse(url_request); | |
| 275 response->redirect_method = redirect_info.new_method; | |
| 276 response->redirect_url = String::From(redirect_info.new_url); | |
| 277 response->redirect_referrer = redirect_info.new_referrer; | |
| 278 | |
| 279 SendResponse(std::move(response)); | |
| 280 | |
| 281 DeleteIfNeeded(); | |
| 282 } | |
| 283 | |
| 284 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) { | |
| 285 DCHECK(url_request == url_request_.get()); | |
| 286 | |
| 287 if (!url_request->status().is_success()) { | |
| 288 SendError(url_request->status().error(), callback_); | |
| 289 callback_ = Callback<void(URLResponsePtr)>(); | |
| 290 DeleteIfNeeded(); | |
| 291 return; | |
| 292 } | |
| 293 | |
| 294 // TODO(darin): Add support for optional MIME sniffing. | |
| 295 | |
| 296 DataPipe data_pipe; | |
| 297 // TODO(darin): Honor given buffer size. | |
| 298 | |
| 299 URLResponsePtr response = MakeURLResponse(url_request); | |
| 300 response->body = std::move(data_pipe.consumer_handle); | |
| 301 response_body_stream_ = std::move(data_pipe.producer_handle); | |
| 302 ListenForPeerClosed(); | |
| 303 | |
| 304 SendResponse(std::move(response)); | |
| 305 | |
| 306 // Start reading... | |
| 307 ReadMore(); | |
| 308 } | |
| 309 | |
| 310 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request, | |
| 311 int bytes_read) { | |
| 312 DCHECK(url_request == url_request_.get()); | |
| 313 | |
| 314 if (url_request->status().is_success()) { | |
| 315 DidRead(static_cast<uint32_t>(bytes_read), false); | |
| 316 } else { | |
| 317 handle_watcher_.Stop(); | |
| 318 pending_write_ = nullptr; // This closes the data pipe. | |
| 319 DeleteIfNeeded(); | |
| 320 return; | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 void URLLoaderImpl::SendError( | |
| 325 int error_code, | |
| 326 const Callback<void(URLResponsePtr)>& callback) { | |
| 327 URLResponsePtr response(URLResponse::New()); | |
| 328 if (url_request_) | |
| 329 response->url = String::From(url_request_->url()); | |
| 330 response->error = MakeNetworkError(error_code); | |
| 331 callback.Run(std::move(response)); | |
| 332 } | |
| 333 | |
| 334 void URLLoaderImpl::SendResponse(URLResponsePtr response) { | |
| 335 Callback<void(URLResponsePtr)> callback; | |
| 336 std::swap(callback_, callback); | |
| 337 callback.Run(std::move(response)); | |
| 338 } | |
| 339 | |
| 340 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) { | |
| 341 // TODO(darin): Handle a bad |result| value. | |
| 342 | |
| 343 // Continue watching the handle in case the peer is closed. | |
| 344 ListenForPeerClosed(); | |
| 345 ReadMore(); | |
| 346 } | |
| 347 | |
| 348 void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) { | |
| 349 url_request_.reset(); | |
| 350 response_body_stream_.reset(); | |
| 351 pending_write_ = nullptr; | |
| 352 DeleteIfNeeded(); | |
| 353 } | |
| 354 | |
| 355 void URLLoaderImpl::ReadMore() { | |
| 356 DCHECK(!pending_write_.get()); | |
| 357 | |
| 358 uint32_t num_bytes; | |
| 359 MojoResult result = NetToMojoPendingBuffer::BeginWrite( | |
| 360 &response_body_stream_, &pending_write_, &num_bytes); | |
| 361 | |
| 362 if (result == MOJO_RESULT_SHOULD_WAIT) { | |
| 363 // The pipe is full. We need to wait for it to have more space. | |
| 364 handle_watcher_.Start(response_body_stream_.get(), | |
| 365 MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, | |
| 366 base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady, | |
| 367 base::Unretained(this))); | |
| 368 return; | |
| 369 } else if (result != MOJO_RESULT_OK) { | |
| 370 // The response body stream is in a bad state. Bail. | |
| 371 // TODO(darin): How should this be communicated to our client? | |
| 372 handle_watcher_.Stop(); | |
| 373 response_body_stream_.reset(); | |
| 374 DeleteIfNeeded(); | |
| 375 return; | |
| 376 } | |
| 377 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes); | |
| 378 | |
| 379 scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(pending_write_.get())); | |
| 380 | |
| 381 int bytes_read; | |
| 382 url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read); | |
| 383 if (url_request_->status().is_io_pending()) { | |
| 384 // Wait for OnReadCompleted. | |
| 385 } else if (url_request_->status().is_success() && bytes_read > 0) { | |
| 386 DidRead(static_cast<uint32_t>(bytes_read), true); | |
| 387 } else { | |
| 388 handle_watcher_.Stop(); | |
| 389 pending_write_->Complete(0); | |
| 390 pending_write_ = nullptr; // This closes the data pipe. | |
| 391 DeleteIfNeeded(); | |
| 392 return; | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) { | |
| 397 DCHECK(url_request_->status().is_success()); | |
| 398 | |
| 399 response_body_bytes_read_ += num_bytes; | |
| 400 response_body_stream_ = pending_write_->Complete(num_bytes); | |
| 401 pending_write_ = nullptr; | |
| 402 | |
| 403 if (completed_synchronously) { | |
| 404 base::MessageLoop::current()->PostTask( | |
| 405 FROM_HERE, | |
| 406 base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr())); | |
| 407 } else { | |
| 408 ReadMore(); | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 void URLLoaderImpl::DeleteIfNeeded() { | |
| 413 bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid(); | |
| 414 if (!connected_ && !has_data_pipe) | |
| 415 delete this; | |
| 416 } | |
| 417 | |
| 418 void URLLoaderImpl::ListenForPeerClosed() { | |
| 419 handle_watcher_.Start(response_body_stream_.get(), | |
| 420 MOJO_HANDLE_SIGNAL_PEER_CLOSED, | |
| 421 MOJO_DEADLINE_INDEFINITE, | |
| 422 base::Bind(&URLLoaderImpl::OnResponseBodyStreamClosed, | |
| 423 base::Unretained(this))); | |
| 424 } | |
| 425 | |
| 426 } // namespace mojo | |
| OLD | NEW |