OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 "content/network/url_loader_impl.h" |
| 6 |
| 7 #include "base/task_scheduler/post_task.h" |
| 8 #include "base/threading/thread_task_runner_handle.h" |
| 9 #include "content/network/net_adapters.h" |
| 10 #include "content/network/network_context.h" |
| 11 #include "content/public/common/referrer.h" |
| 12 #include "content/public/common/resource_response.h" |
| 13 #include "net/base/elements_upload_data_stream.h" |
| 14 #include "net/base/load_flags.h" |
| 15 #include "net/base/upload_bytes_element_reader.h" |
| 16 #include "net/base/upload_file_element_reader.h" |
| 17 #include "net/url_request/url_request_context.h" |
| 18 |
| 19 namespace content { |
| 20 |
| 21 namespace { |
| 22 constexpr size_t kDefaultAllocationSize = 512 * 1024; |
| 23 |
| 24 // TODO: this duplicates ResourceDispatcherHostImpl::BuildLoadFlagsForRequest. |
| 25 int BuildLoadFlagsForRequest(const ResourceRequest& request, |
| 26 bool is_sync_load) { |
| 27 int load_flags = request.load_flags; |
| 28 |
| 29 // Although EV status is irrelevant to sub-frames and sub-resources, we have |
| 30 // to perform EV certificate verification on all resources because an HTTP |
| 31 // keep-alive connection created to load a sub-frame or a sub-resource could |
| 32 // be reused to load a main frame. |
| 33 load_flags |= net::LOAD_VERIFY_EV_CERT; |
| 34 if (request.resource_type == RESOURCE_TYPE_MAIN_FRAME) { |
| 35 load_flags |= net::LOAD_MAIN_FRAME_DEPRECATED; |
| 36 } else if (request.resource_type == RESOURCE_TYPE_PREFETCH) { |
| 37 load_flags |= net::LOAD_PREFETCH; |
| 38 } |
| 39 |
| 40 if (is_sync_load) |
| 41 load_flags |= net::LOAD_IGNORE_LIMITS; |
| 42 |
| 43 return load_flags; |
| 44 } |
| 45 |
| 46 // TODO: this duplicates some of PopulateResourceResponse in |
| 47 // content/browser/loader/resource_loader.cc |
| 48 void PopulateResourceResponse(net::URLRequest* request, |
| 49 ResourceResponse* response) { |
| 50 response->head.request_time = request->request_time(); |
| 51 response->head.response_time = request->response_time(); |
| 52 response->head.headers = request->response_headers(); |
| 53 request->GetCharset(&response->head.charset); |
| 54 response->head.content_length = request->GetExpectedContentSize(); |
| 55 request->GetMimeType(&response->head.mime_type); |
| 56 net::HttpResponseInfo response_info = request->response_info(); |
| 57 response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy; |
| 58 response->head.was_alpn_negotiated = response_info.was_alpn_negotiated; |
| 59 response->head.alpn_negotiated_protocol = |
| 60 response_info.alpn_negotiated_protocol; |
| 61 response->head.connection_info = response_info.connection_info; |
| 62 response->head.socket_address = response_info.socket_address; |
| 63 |
| 64 response->head.effective_connection_type = |
| 65 net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN; |
| 66 } |
| 67 |
| 68 // A subclass of net::UploadBytesElementReader which owns |
| 69 // ResourceRequestBodyImpl. |
| 70 class BytesElementReader : public net::UploadBytesElementReader { |
| 71 public: |
| 72 BytesElementReader(ResourceRequestBodyImpl* resource_request_body, |
| 73 const ResourceRequestBodyImpl::Element& element) |
| 74 : net::UploadBytesElementReader(element.bytes(), element.length()), |
| 75 resource_request_body_(resource_request_body) { |
| 76 DCHECK_EQ(ResourceRequestBodyImpl::Element::TYPE_BYTES, element.type()); |
| 77 } |
| 78 |
| 79 ~BytesElementReader() override {} |
| 80 |
| 81 private: |
| 82 scoped_refptr<ResourceRequestBodyImpl> resource_request_body_; |
| 83 |
| 84 DISALLOW_COPY_AND_ASSIGN(BytesElementReader); |
| 85 }; |
| 86 |
| 87 // A subclass of net::UploadFileElementReader which owns |
| 88 // ResourceRequestBodyImpl. |
| 89 // This class is necessary to ensure the BlobData and any attached shareable |
| 90 // files survive until upload completion. |
| 91 class FileElementReader : public net::UploadFileElementReader { |
| 92 public: |
| 93 FileElementReader(ResourceRequestBodyImpl* resource_request_body, |
| 94 base::TaskRunner* task_runner, |
| 95 const ResourceRequestBodyImpl::Element& element) |
| 96 : net::UploadFileElementReader(task_runner, |
| 97 element.path(), |
| 98 element.offset(), |
| 99 element.length(), |
| 100 element.expected_modification_time()), |
| 101 resource_request_body_(resource_request_body) { |
| 102 DCHECK_EQ(ResourceRequestBodyImpl::Element::TYPE_FILE, element.type()); |
| 103 } |
| 104 |
| 105 ~FileElementReader() override {} |
| 106 |
| 107 private: |
| 108 scoped_refptr<ResourceRequestBodyImpl> resource_request_body_; |
| 109 |
| 110 DISALLOW_COPY_AND_ASSIGN(FileElementReader); |
| 111 }; |
| 112 |
| 113 // TODO: copied from content/browser/loader/upload_data_stream_builder.cc. |
| 114 std::unique_ptr<net::UploadDataStream> CreateUploadDataStream( |
| 115 ResourceRequestBodyImpl* body, |
| 116 base::SequencedTaskRunner* file_task_runner) { |
| 117 std::vector<std::unique_ptr<net::UploadElementReader>> element_readers; |
| 118 for (const auto& element : *body->elements()) { |
| 119 switch (element.type()) { |
| 120 case ResourceRequestBodyImpl::Element::TYPE_BYTES: |
| 121 element_readers.push_back( |
| 122 base::MakeUnique<BytesElementReader>(body, element)); |
| 123 break; |
| 124 case ResourceRequestBodyImpl::Element::TYPE_FILE: |
| 125 element_readers.push_back(base::MakeUnique<FileElementReader>( |
| 126 body, file_task_runner, element)); |
| 127 break; |
| 128 case ResourceRequestBodyImpl::Element::TYPE_FILE_FILESYSTEM: |
| 129 NOTIMPLEMENTED(); |
| 130 break; |
| 131 case ResourceRequestBodyImpl::Element::TYPE_BLOB: { |
| 132 NOTIMPLEMENTED(); |
| 133 break; |
| 134 } |
| 135 case ResourceRequestBodyImpl::Element::TYPE_DISK_CACHE_ENTRY: |
| 136 case ResourceRequestBodyImpl::Element::TYPE_BYTES_DESCRIPTION: |
| 137 case ResourceRequestBodyImpl::Element::TYPE_UNKNOWN: |
| 138 NOTREACHED(); |
| 139 break; |
| 140 } |
| 141 } |
| 142 |
| 143 return base::MakeUnique<net::ElementsUploadDataStream>( |
| 144 std::move(element_readers), body->identifier()); |
| 145 } |
| 146 |
| 147 } // namespace |
| 148 |
| 149 URLLoaderImpl::URLLoaderImpl(NetworkContext* context, |
| 150 mojom::URLLoaderRequest url_loader_request, |
| 151 const ResourceRequest& request, |
| 152 mojom::URLLoaderClientPtr url_loader_client) |
| 153 : context_(context), |
| 154 connected_(true), |
| 155 binding_(this, std::move(url_loader_request)), |
| 156 url_loader_client_(std::move(url_loader_client)), |
| 157 writable_handle_watcher_(FROM_HERE, |
| 158 mojo::SimpleWatcher::ArmingPolicy::MANUAL), |
| 159 peer_closed_handle_watcher_(FROM_HERE, |
| 160 mojo::SimpleWatcher::ArmingPolicy::MANUAL), |
| 161 weak_ptr_factory_(this) { |
| 162 binding_.set_connection_error_handler( |
| 163 base::Bind(&URLLoaderImpl::OnConnectionError, base::Unretained(this))); |
| 164 |
| 165 url_request_ = context_->url_request_context()->CreateRequest( |
| 166 GURL(request.url), net::DEFAULT_PRIORITY, this); |
| 167 url_request_->set_method(request.method); |
| 168 |
| 169 const Referrer referrer(request.referrer, request.referrer_policy); |
| 170 Referrer::SetReferrerForRequest(url_request_.get(), referrer); |
| 171 |
| 172 net::HttpRequestHeaders headers; |
| 173 headers.AddHeadersFromString(request.headers); |
| 174 url_request_->SetExtraRequestHeaders(headers); |
| 175 |
| 176 // Resolve elements from request_body and prepare upload data. |
| 177 if (request.request_body.get()) { |
| 178 scoped_refptr<base::SequencedTaskRunner> task_runner = |
| 179 base::CreateSequencedTaskRunnerWithTraits( |
| 180 base::TaskTraits().MayBlock().WithPriority( |
| 181 base::TaskPriority::USER_VISIBLE)); |
| 182 url_request_->set_upload( |
| 183 CreateUploadDataStream(request.request_body.get(), task_runner.get())); |
| 184 } |
| 185 |
| 186 int load_flags = BuildLoadFlagsForRequest(request, false); |
| 187 url_request_->SetLoadFlags(load_flags); |
| 188 |
| 189 url_request_->Start(); |
| 190 } |
| 191 |
| 192 URLLoaderImpl::~URLLoaderImpl() {} |
| 193 |
| 194 void URLLoaderImpl::Cleanup() { |
| 195 // The associated network context is going away and we have to destroy |
| 196 // net::URLRequest hold by this loader. |
| 197 delete this; |
| 198 } |
| 199 |
| 200 void URLLoaderImpl::FollowRedirect() { |
| 201 if (!url_request_) { |
| 202 NotifyCompleted(net::ERR_UNEXPECTED); |
| 203 return; |
| 204 } |
| 205 |
| 206 url_request_->FollowDeferredRedirect(); |
| 207 } |
| 208 |
| 209 void URLLoaderImpl::SetPriority(net::RequestPriority priority, |
| 210 int32_t intra_priority_value) { |
| 211 NOTIMPLEMENTED(); |
| 212 } |
| 213 |
| 214 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request, |
| 215 const net::RedirectInfo& redirect_info, |
| 216 bool* defer_redirect) { |
| 217 DCHECK(url_request == url_request_.get()); |
| 218 DCHECK(url_request->status().is_success()); |
| 219 |
| 220 // Send the redirect response to the client, allowing them to inspect it and |
| 221 // optionally follow the redirect. |
| 222 *defer_redirect = true; |
| 223 |
| 224 scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| 225 PopulateResourceResponse(url_request_.get(), response.get()); |
| 226 |
| 227 url_loader_client_->OnReceiveRedirect(redirect_info, response->head); |
| 228 } |
| 229 |
| 230 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) { |
| 231 DCHECK(url_request == url_request_.get()); |
| 232 |
| 233 // TODO: Add support for optional MIME sniffing. |
| 234 |
| 235 scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| 236 PopulateResourceResponse(url_request_.get(), response.get()); |
| 237 |
| 238 mojom::DownloadedTempFilePtr downloaded_file_ptr; |
| 239 url_loader_client_->OnReceiveResponse(response->head, |
| 240 std::move(downloaded_file_ptr)); |
| 241 |
| 242 net::IOBufferWithSize* metadata = url_request->response_info().metadata.get(); |
| 243 if (metadata) { |
| 244 const uint8_t* data = reinterpret_cast<const uint8_t*>(metadata->data()); |
| 245 |
| 246 url_loader_client_->OnReceiveCachedMetadata( |
| 247 std::vector<uint8_t>(data, data + metadata->size())); |
| 248 } |
| 249 |
| 250 MojoCreateDataPipeOptions options; |
| 251 options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| 252 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; |
| 253 options.element_num_bytes = 1; |
| 254 options.capacity_num_bytes = kDefaultAllocationSize; |
| 255 mojo::DataPipe data_pipe(options); |
| 256 |
| 257 DCHECK(data_pipe.producer_handle.is_valid()); |
| 258 DCHECK(data_pipe.consumer_handle.is_valid()); |
| 259 |
| 260 response_body_stream_ = std::move(data_pipe.producer_handle); |
| 261 response_body_consumer_handle_ = std::move(data_pipe.consumer_handle); |
| 262 peer_closed_handle_watcher_.Watch( |
| 263 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, |
| 264 base::Bind(&URLLoaderImpl::OnResponseBodyStreamClosed, |
| 265 base::Unretained(this))); |
| 266 peer_closed_handle_watcher_.ArmOrNotify(); |
| 267 |
| 268 writable_handle_watcher_.Watch( |
| 269 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, |
| 270 base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady, |
| 271 base::Unretained(this))); |
| 272 |
| 273 // Start reading... |
| 274 ReadMore(); |
| 275 } |
| 276 |
| 277 void URLLoaderImpl::ReadMore() { |
| 278 DCHECK(!pending_write_.get()); |
| 279 |
| 280 uint32_t num_bytes; |
| 281 // TODO: we should use the abstractions in MojoAsyncResourceHandler. |
| 282 MojoResult result = NetToMojoPendingBuffer::BeginWrite( |
| 283 &response_body_stream_, &pending_write_, &num_bytes); |
| 284 if (result == MOJO_RESULT_SHOULD_WAIT) { |
| 285 // The pipe is full. We need to wait for it to have more space. |
| 286 writable_handle_watcher_.ArmOrNotify(); |
| 287 return; |
| 288 } else if (result != MOJO_RESULT_OK) { |
| 289 // The response body stream is in a bad state. Bail. |
| 290 // TODO: How should this be communicated to our client? |
| 291 writable_handle_watcher_.Cancel(); |
| 292 response_body_stream_.reset(); |
| 293 DeleteIfNeeded(); |
| 294 return; |
| 295 } |
| 296 |
| 297 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes); |
| 298 scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(pending_write_.get())); |
| 299 int bytes_read; |
| 300 url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read); |
| 301 if (url_request_->status().is_io_pending()) { |
| 302 // Wait for OnReadCompleted. |
| 303 } else if (url_request_->status().is_success() && bytes_read > 0) { |
| 304 SendDataPipeIfNecessary(); |
| 305 DidRead(static_cast<uint32_t>(bytes_read), true); |
| 306 } else { |
| 307 NotifyCompleted(net::OK); |
| 308 writable_handle_watcher_.Cancel(); |
| 309 pending_write_->Complete(0); |
| 310 pending_write_ = nullptr; // This closes the data pipe. |
| 311 DeleteIfNeeded(); |
| 312 return; |
| 313 } |
| 314 } |
| 315 |
| 316 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) { |
| 317 DCHECK(url_request_->status().is_success()); |
| 318 response_body_stream_ = pending_write_->Complete(num_bytes); |
| 319 pending_write_ = nullptr; |
| 320 if (completed_synchronously) { |
| 321 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 322 FROM_HERE, |
| 323 base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr())); |
| 324 } else { |
| 325 ReadMore(); |
| 326 } |
| 327 } |
| 328 |
| 329 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request, |
| 330 int bytes_read) { |
| 331 DCHECK(url_request == url_request_.get()); |
| 332 |
| 333 if (!url_request->status().is_success()) { |
| 334 writable_handle_watcher_.Cancel(); |
| 335 pending_write_ = nullptr; // This closes the data pipe. |
| 336 DeleteIfNeeded(); |
| 337 return; |
| 338 } |
| 339 |
| 340 SendDataPipeIfNecessary(); |
| 341 |
| 342 DidRead(static_cast<uint32_t>(bytes_read), false); |
| 343 } |
| 344 |
| 345 void URLLoaderImpl::NotifyCompleted(int error_code) { |
| 346 ResourceRequestCompletionStatus request_complete_data; |
| 347 request_complete_data.error_code = error_code; |
| 348 request_complete_data.exists_in_cache = |
| 349 url_request_->response_info().was_cached; |
| 350 request_complete_data.completion_time = base::TimeTicks::Now(); |
| 351 request_complete_data.encoded_data_length = |
| 352 url_request_->GetTotalReceivedBytes(); |
| 353 request_complete_data.encoded_body_length = url_request_->GetRawBodyBytes(); |
| 354 |
| 355 url_loader_client_->OnComplete(request_complete_data); |
| 356 DeleteIfNeeded(); |
| 357 } |
| 358 |
| 359 void URLLoaderImpl::SendDataPipeIfNecessary() { |
| 360 if (response_body_consumer_handle_.is_valid()) { |
| 361 // Send the data pipe on the first OnReadCompleted call. |
| 362 url_loader_client_->OnStartLoadingResponseBody( |
| 363 std::move(response_body_consumer_handle_)); |
| 364 } |
| 365 } |
| 366 |
| 367 void URLLoaderImpl::OnConnectionError() { |
| 368 connected_ = false; |
| 369 DeleteIfNeeded(); |
| 370 } |
| 371 |
| 372 void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) { |
| 373 url_request_.reset(); |
| 374 response_body_stream_.reset(); |
| 375 pending_write_ = nullptr; |
| 376 DeleteIfNeeded(); |
| 377 } |
| 378 |
| 379 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) { |
| 380 // TODO: Handle a bad |result| value. |
| 381 DCHECK_EQ(result, MOJO_RESULT_OK); |
| 382 ReadMore(); |
| 383 } |
| 384 |
| 385 void URLLoaderImpl::DeleteIfNeeded() { |
| 386 bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid(); |
| 387 if (!connected_ && !has_data_pipe) |
| 388 delete this; |
| 389 } |
| 390 |
| 391 } // namespace content |
OLD | NEW |