OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/loader/navigation_resource_handler.h" | 5 #include "content/browser/loader/navigation_resource_handler.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "content/browser/loader/navigation_url_loader_impl_core.h" | 11 #include "content/browser/loader/navigation_url_loader_impl_core.h" |
12 #include "content/browser/loader/netlog_observer.h" | 12 #include "content/browser/loader/netlog_observer.h" |
13 #include "content/browser/loader/resource_controller.h" | 13 #include "content/browser/loader/resource_controller.h" |
14 #include "content/browser/loader/resource_loader.h" | 14 #include "content/browser/loader/resource_loader.h" |
15 #include "content/browser/loader/resource_request_info_impl.h" | 15 #include "content/browser/loader/resource_request_info_impl.h" |
16 #include "content/browser/resource_context_impl.h" | 16 #include "content/browser/resource_context_impl.h" |
17 #include "content/browser/streams/stream.h" | 17 #include "content/browser/streams/stream.h" |
18 #include "content/browser/streams/stream_context.h" | 18 #include "content/browser/streams/stream_context.h" |
19 #include "content/public/browser/navigation_data.h" | 19 #include "content/public/browser/navigation_data.h" |
20 #include "content/public/browser/resource_dispatcher_host_delegate.h" | 20 #include "content/public/browser/resource_dispatcher_host_delegate.h" |
21 #include "content/public/browser/ssl_status.h" | 21 #include "content/public/browser/ssl_status.h" |
22 #include "content/public/browser/stream_handle.h" | 22 #include "content/public/browser/stream_handle.h" |
23 #include "content/public/common/resource_response.h" | 23 #include "content/public/common/resource_response.h" |
| 24 #include "net/base/io_buffer.h" |
| 25 #include "net/base/mime_sniffer.h" |
24 #include "net/base/net_errors.h" | 26 #include "net/base/net_errors.h" |
25 #include "net/url_request/url_request.h" | 27 #include "net/url_request/url_request.h" |
26 | 28 |
27 namespace content { | 29 namespace content { |
28 | 30 |
| 31 namespace { |
| 32 |
| 33 // TODO(kinuko): Most of the code below is dup'ed from MojoAsyncResourceHandler, |
| 34 // we should unify the implementation. |
| 35 |
| 36 // For MimeSniffingResourceHandler. |
| 37 constexpr size_t kMinAllocationSize = 2 * net::kMaxBytesToSniff; |
| 38 |
| 39 constexpr size_t kMaxChunkSize = 32 * 1024; |
| 40 |
| 41 constexpr size_t kDefaultAllocationSize = 512 * 1024; |
| 42 |
| 43 } // namespace |
| 44 |
| 45 // This class is for sharing the ownership of a ScopedDataPipeProducerHandle |
| 46 // between WriterIOBuffer and other class. |
| 47 class NavigationResourceHandler::SharedWriter final |
| 48 : public base::RefCountedThreadSafe<SharedWriter> { |
| 49 public: |
| 50 explicit SharedWriter(mojo::ScopedDataPipeProducerHandle writer) |
| 51 : writer_(std::move(writer)) {} |
| 52 mojo::DataPipeProducerHandle writer() { return writer_.get(); } |
| 53 |
| 54 private: |
| 55 friend class base::RefCountedThreadSafe<SharedWriter>; |
| 56 ~SharedWriter() {} |
| 57 |
| 58 const mojo::ScopedDataPipeProducerHandle writer_; |
| 59 |
| 60 DISALLOW_COPY_AND_ASSIGN(SharedWriter); |
| 61 }; |
| 62 |
| 63 // This class is a IOBuffer subclass for data gotten from a |
| 64 // ScopedDataPipeProducerHandle. |
| 65 class NavigationResourceHandler::WriterIOBuffer final |
| 66 : public net::IOBufferWithSize { |
| 67 public: |
| 68 // |data| and |size| should be gotten from |writer| via BeginWriteDataRaw. |
| 69 // They will be accesible via IOBuffer methods. As |writer| is stored in this |
| 70 // instance, |data| will be kept valid as long as the following conditions |
| 71 // hold: |
| 72 // 1. |data| is not invalidated via EndWriteDataRaw. |
| 73 // 2. |this| instance is alive. |
| 74 WriterIOBuffer(scoped_refptr<SharedWriter> writer, void* data, size_t size) |
| 75 : net::IOBufferWithSize(static_cast<char*>(data), size), |
| 76 writer_(std::move(writer)) {} |
| 77 |
| 78 private: |
| 79 ~WriterIOBuffer() override { |
| 80 // Avoid deleting |data_| in the IOBuffer destructor. |
| 81 data_ = nullptr; |
| 82 } |
| 83 |
| 84 // This member is for keeping the writer alive. |
| 85 scoped_refptr<SharedWriter> writer_; |
| 86 |
| 87 DISALLOW_COPY_AND_ASSIGN(WriterIOBuffer); |
| 88 }; |
| 89 |
29 void NavigationResourceHandler::GetSSLStatusForRequest( | 90 void NavigationResourceHandler::GetSSLStatusForRequest( |
30 const GURL& url, | 91 const GURL& url, |
31 const net::SSLInfo& ssl_info, | 92 const net::SSLInfo& ssl_info, |
32 int child_id, | 93 int child_id, |
33 SSLStatus* ssl_status) { | 94 SSLStatus* ssl_status) { |
34 DCHECK(ssl_info.cert); | 95 DCHECK(ssl_info.cert); |
35 *ssl_status = SSLStatus(ssl_info); | 96 *ssl_status = SSLStatus(ssl_info); |
36 } | 97 } |
37 | 98 |
38 NavigationResourceHandler::NavigationResourceHandler( | 99 NavigationResourceHandler::NavigationResourceHandler( |
39 net::URLRequest* request, | 100 net::URLRequest* request, |
40 NavigationURLLoaderImplCore* core, | 101 NavigationURLLoaderImplCore* core, |
41 ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate) | 102 ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate) |
42 : ResourceHandler(request), | 103 : ResourceHandler(request), |
43 core_(core), | 104 core_(core), |
44 resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate) { | 105 resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate), |
| 106 handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL) { |
45 core_->set_resource_handler(this); | 107 core_->set_resource_handler(this); |
46 writer_.set_immediate_mode(true); | |
47 } | 108 } |
48 | 109 |
49 NavigationResourceHandler::~NavigationResourceHandler() { | 110 NavigationResourceHandler::~NavigationResourceHandler() { |
50 if (core_) { | 111 if (core_) { |
51 core_->NotifyRequestFailed(false, net::ERR_ABORTED); | 112 core_->NotifyRequestFailed(false, net::ERR_ABORTED); |
52 DetachFromCore(); | 113 DetachFromCore(); |
53 } | 114 } |
54 } | 115 } |
55 | 116 |
56 void NavigationResourceHandler::Cancel() { | 117 void NavigationResourceHandler::Cancel() { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 } | 149 } |
89 | 150 |
90 void NavigationResourceHandler::OnResponseStarted( | 151 void NavigationResourceHandler::OnResponseStarted( |
91 ResourceResponse* response, | 152 ResourceResponse* response, |
92 std::unique_ptr<ResourceController> controller) { | 153 std::unique_ptr<ResourceController> controller) { |
93 DCHECK(core_); | 154 DCHECK(core_); |
94 DCHECK(!has_controller()); | 155 DCHECK(!has_controller()); |
95 | 156 |
96 ResourceRequestInfoImpl* info = GetRequestInfo(); | 157 ResourceRequestInfoImpl* info = GetRequestInfo(); |
97 | 158 |
98 StreamContext* stream_context = | 159 mojo::ScopedDataPipeConsumerHandle data_consumer_handle; |
99 GetStreamContextForResourceContext(info->GetContext()); | 160 InitializeDataPipe(&data_consumer_handle); |
100 writer_.InitializeStream( | |
101 stream_context->registry(), request()->url().GetOrigin(), | |
102 base::Bind(&NavigationResourceHandler::OutOfBandCancel, | |
103 base::Unretained(this), net::ERR_ABORTED, | |
104 true /* tell_renderer */)); | |
105 | 161 |
106 NetLogObserver::PopulateResponseInfo(request(), response); | 162 NetLogObserver::PopulateResponseInfo(request(), response); |
107 response->head.encoded_data_length = request()->raw_header_size(); | 163 response->head.encoded_data_length = request()->raw_header_size(); |
108 | 164 |
109 std::unique_ptr<NavigationData> cloned_data; | 165 std::unique_ptr<NavigationData> cloned_data; |
110 if (resource_dispatcher_host_delegate_) { | 166 if (resource_dispatcher_host_delegate_) { |
111 // Ask the embedder for a NavigationData instance. | 167 // Ask the embedder for a NavigationData instance. |
112 NavigationData* navigation_data = | 168 NavigationData* navigation_data = |
113 resource_dispatcher_host_delegate_->GetNavigationData(request()); | 169 resource_dispatcher_host_delegate_->GetNavigationData(request()); |
114 | 170 |
115 // Clone the embedder's NavigationData before moving it to the UI thread. | 171 // Clone the embedder's NavigationData before moving it to the UI thread. |
116 if (navigation_data) | 172 if (navigation_data) |
117 cloned_data = navigation_data->Clone(); | 173 cloned_data = navigation_data->Clone(); |
118 } | 174 } |
119 | 175 |
120 SSLStatus ssl_status; | 176 SSLStatus ssl_status; |
121 if (request()->ssl_info().cert.get()) { | 177 if (request()->ssl_info().cert.get()) { |
122 GetSSLStatusForRequest(request()->url(), request()->ssl_info(), | 178 GetSSLStatusForRequest(request()->url(), request()->ssl_info(), |
123 info->GetChildID(), &ssl_status); | 179 info->GetChildID(), &ssl_status); |
124 } | 180 } |
125 | 181 |
126 core_->NotifyResponseStarted(response, writer_.stream()->CreateHandle(), | 182 core_->NotifyResponseStarted(response, std::move(data_consumer_handle), |
127 ssl_status, std::move(cloned_data), | 183 ssl_status, std::move(cloned_data), |
128 info->GetGlobalRequestID(), info->IsDownload(), | 184 info->GetGlobalRequestID(), info->IsDownload(), |
129 info->is_stream()); | 185 info->is_stream()); |
130 // Don't defer stream based requests. This includes requests initiated via | 186 // Don't defer stream based requests. This includes requests initiated via |
131 // mime type sniffing, etc. | 187 // mime type sniffing, etc. |
132 // TODO(ananta) | 188 // TODO(ananta) |
133 // Make sure that the requests go through the throttle checks. Currently this | 189 // Make sure that the requests go through the throttle checks. Currently this |
134 // does not work as the InterceptingResourceHandler is above us and hence it | 190 // does not work as the InterceptingResourceHandler is above us and hence it |
135 // does not expect the old handler to defer the request. | 191 // does not expect the old handler to defer the request. |
136 // TODO(clamy): We should also make the downloads wait on the | 192 // TODO(clamy): We should also make the downloads wait on the |
(...skipping 15 matching lines...) Expand all Loading... |
152 std::unique_ptr<ResourceController> controller) { | 208 std::unique_ptr<ResourceController> controller) { |
153 DCHECK(!has_controller()); | 209 DCHECK(!has_controller()); |
154 controller->Resume(); | 210 controller->Resume(); |
155 } | 211 } |
156 | 212 |
157 void NavigationResourceHandler::OnWillRead( | 213 void NavigationResourceHandler::OnWillRead( |
158 scoped_refptr<net::IOBuffer>* buf, | 214 scoped_refptr<net::IOBuffer>* buf, |
159 int* buf_size, | 215 int* buf_size, |
160 std::unique_ptr<ResourceController> controller) { | 216 std::unique_ptr<ResourceController> controller) { |
161 DCHECK(!has_controller()); | 217 DCHECK(!has_controller()); |
162 writer_.OnWillRead(buf, buf_size, -1); | 218 |
| 219 DCHECK(shared_writer_); |
| 220 DCHECK(!buffer_); |
| 221 DCHECK_EQ(0u, buffer_offset_); |
| 222 |
| 223 bool first_call = first_read_; |
| 224 |
| 225 if (first_read_) { |
| 226 first_read_ = false; |
| 227 handle_watcher_.Watch(shared_writer_->writer(), MOJO_HANDLE_SIGNAL_WRITABLE, |
| 228 base::Bind(&NavigationResourceHandler::OnWritable, |
| 229 base::Unretained(this))); |
| 230 handle_watcher_.ArmOrNotify(); |
| 231 } |
| 232 |
| 233 bool defer = false; |
| 234 if (!AllocateWriterIOBuffer(&buffer_, &defer)) { |
| 235 controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
| 236 return; |
| 237 } |
| 238 |
| 239 if (defer) { |
| 240 parent_buffer_ = buf; |
| 241 parent_buffer_size_ = buf_size; |
| 242 HoldController(std::move(controller)); |
| 243 request()->LogBlockedBy("MojoStreamWriter"); |
| 244 did_defer_on_will_read_ = true; |
| 245 return; |
| 246 } |
| 247 |
| 248 // The first call to OnWillRead must return a buffer of at least |
| 249 // kMinAllocationSize. If the Mojo buffer is too small, need to allocate an |
| 250 // intermediary buffer. |
| 251 if (first_call && static_cast<size_t>(buffer_->size()) < kMinAllocationSize) { |
| 252 // The allocated buffer is too small, so need to create an intermediary one. |
| 253 if (EndWrite(0) != MOJO_RESULT_OK) { |
| 254 controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
| 255 return; |
| 256 } |
| 257 DCHECK(!is_using_io_buffer_not_from_writer_); |
| 258 is_using_io_buffer_not_from_writer_ = true; |
| 259 buffer_ = new net::IOBufferWithSize(kMinAllocationSize); |
| 260 } |
| 261 |
| 262 *buf = buffer_; |
| 263 *buf_size = buffer_->size(); |
163 controller->Resume(); | 264 controller->Resume(); |
164 } | 265 } |
165 | 266 |
166 void NavigationResourceHandler::OnReadCompleted( | 267 void NavigationResourceHandler::OnReadCompleted( |
167 int bytes_read, | 268 int bytes_read, |
168 std::unique_ptr<ResourceController> controller) { | 269 std::unique_ptr<ResourceController> controller) { |
169 DCHECK(!has_controller()); | 270 DCHECK(!has_controller()); |
170 writer_.OnReadCompleted(bytes_read, | 271 DCHECK_GE(bytes_read, 0); |
171 base::Bind(&ResourceController::Resume, | 272 DCHECK(buffer_); |
172 base::Passed(std::move(controller)))); | 273 |
| 274 if (bytes_read == 0) { |
| 275 // Note that |buffer_| is not cleared here, which will cause a DCHECK on |
| 276 // subsequent OnWillRead calls. |
| 277 controller->Resume(); |
| 278 return; |
| 279 } |
| 280 |
| 281 if (is_using_io_buffer_not_from_writer_) { |
| 282 // Couldn't allocate a large enough buffer on the data pipe in OnWillRead. |
| 283 DCHECK_EQ(0u, buffer_bytes_read_); |
| 284 buffer_bytes_read_ = bytes_read; |
| 285 bool defer = false; |
| 286 if (!CopyReadDataToDataPipe(&defer)) { |
| 287 controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
| 288 return; |
| 289 } |
| 290 if (defer) { |
| 291 request()->LogBlockedBy("NavigationResourceHandler"); |
| 292 did_defer_on_writing_ = true; |
| 293 HoldController(std::move(controller)); |
| 294 return; |
| 295 } |
| 296 controller->Resume(); |
| 297 return; |
| 298 } |
| 299 |
| 300 if (EndWrite(bytes_read) != MOJO_RESULT_OK) { |
| 301 controller->Cancel(); |
| 302 return; |
| 303 } |
| 304 |
| 305 buffer_ = nullptr; |
| 306 controller->Resume(); |
173 } | 307 } |
174 | 308 |
175 void NavigationResourceHandler::OnResponseCompleted( | 309 void NavigationResourceHandler::OnResponseCompleted( |
176 const net::URLRequestStatus& status, | 310 const net::URLRequestStatus& status, |
177 std::unique_ptr<ResourceController> controller) { | 311 std::unique_ptr<ResourceController> controller) { |
178 // If the request has already committed, close the stream and leave it as-is. | 312 if (shared_writer_) { |
179 if (writer_.stream()) { | 313 shared_writer_ = nullptr; |
180 writer_.Finalize(status.error()); | 314 buffer_ = nullptr; |
| 315 handle_watcher_.Cancel(); |
181 controller->Resume(); | 316 controller->Resume(); |
182 return; | 317 return; |
183 } | 318 } |
184 | 319 |
185 if (core_) { | 320 if (core_) { |
186 DCHECK_NE(net::OK, status.error()); | 321 DCHECK_NE(net::OK, status.error()); |
187 core_->NotifyRequestFailed(request()->response_info().was_cached, | 322 core_->NotifyRequestFailed(request()->response_info().was_cached, |
188 status.error()); | 323 status.error()); |
189 DetachFromCore(); | 324 DetachFromCore(); |
190 } | 325 } |
191 controller->Resume(); | 326 controller->Resume(); |
192 } | 327 } |
193 | 328 |
194 void NavigationResourceHandler::OnDataDownloaded(int bytes_downloaded) { | 329 void NavigationResourceHandler::OnDataDownloaded(int bytes_downloaded) { |
195 NOTREACHED(); | 330 NOTREACHED(); |
196 } | 331 } |
197 | 332 |
198 void NavigationResourceHandler::DetachFromCore() { | 333 void NavigationResourceHandler::DetachFromCore() { |
199 DCHECK(core_); | 334 DCHECK(core_); |
200 core_->set_resource_handler(nullptr); | 335 core_->set_resource_handler(nullptr); |
201 core_ = nullptr; | 336 core_ = nullptr; |
202 } | 337 } |
203 | 338 |
| 339 void NavigationResourceHandler::InitializeDataPipe( |
| 340 mojo::ScopedDataPipeConsumerHandle* consumer_handle) { |
| 341 DCHECK(!shared_writer_); |
| 342 |
| 343 MojoCreateDataPipeOptions options; |
| 344 options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| 345 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; |
| 346 options.element_num_bytes = 1; |
| 347 options.capacity_num_bytes = kDefaultAllocationSize; |
| 348 mojo::DataPipe data_pipe(options); |
| 349 |
| 350 DCHECK(data_pipe.producer_handle.is_valid()); |
| 351 DCHECK(data_pipe.consumer_handle.is_valid()); |
| 352 |
| 353 *consumer_handle = std::move(data_pipe.consumer_handle); |
| 354 shared_writer_ = new SharedWriter(std::move(data_pipe.producer_handle)); |
| 355 } |
| 356 |
| 357 MojoResult NavigationResourceHandler::BeginWrite(void** data, |
| 358 uint32_t* available) { |
| 359 MojoResult result = mojo::BeginWriteDataRaw( |
| 360 shared_writer_->writer(), data, available, MOJO_WRITE_DATA_FLAG_NONE); |
| 361 if (result == MOJO_RESULT_OK) |
| 362 *available = std::min(*available, static_cast<uint32_t>(kMaxChunkSize)); |
| 363 else if (result == MOJO_RESULT_SHOULD_WAIT) |
| 364 handle_watcher_.ArmOrNotify(); |
| 365 return result; |
| 366 } |
| 367 |
| 368 MojoResult NavigationResourceHandler::EndWrite(uint32_t written) { |
| 369 MojoResult result = mojo::EndWriteDataRaw(shared_writer_->writer(), written); |
| 370 if (result == MOJO_RESULT_OK) |
| 371 handle_watcher_.ArmOrNotify(); |
| 372 return result; |
| 373 } |
| 374 |
| 375 bool NavigationResourceHandler::CopyReadDataToDataPipe(bool* defer) { |
| 376 while (buffer_bytes_read_ > 0) { |
| 377 scoped_refptr<net::IOBufferWithSize> dest; |
| 378 if (!AllocateWriterIOBuffer(&dest, defer)) |
| 379 return false; |
| 380 if (*defer) |
| 381 return true; |
| 382 |
| 383 size_t copied_size = |
| 384 std::min(buffer_bytes_read_, static_cast<size_t>(dest->size())); |
| 385 memcpy(dest->data(), buffer_->data() + buffer_offset_, copied_size); |
| 386 buffer_offset_ += copied_size; |
| 387 buffer_bytes_read_ -= copied_size; |
| 388 if (EndWrite(copied_size) != MOJO_RESULT_OK) |
| 389 return false; |
| 390 } |
| 391 |
| 392 // All bytes are copied. |
| 393 buffer_ = nullptr; |
| 394 buffer_offset_ = 0; |
| 395 is_using_io_buffer_not_from_writer_ = false; |
| 396 return true; |
| 397 } |
| 398 |
| 399 bool NavigationResourceHandler::AllocateWriterIOBuffer( |
| 400 scoped_refptr<net::IOBufferWithSize>* buf, |
| 401 bool* defer) { |
| 402 void* data = nullptr; |
| 403 uint32_t available = 0; |
| 404 MojoResult result = BeginWrite(&data, &available); |
| 405 if (result == MOJO_RESULT_SHOULD_WAIT) { |
| 406 *defer = true; |
| 407 return true; |
| 408 } |
| 409 if (result != MOJO_RESULT_OK) |
| 410 return false; |
| 411 DCHECK_GT(available, 0u); |
| 412 *buf = new WriterIOBuffer(shared_writer_, data, available); |
| 413 return true; |
| 414 } |
| 415 |
| 416 void NavigationResourceHandler::OnWritable(MojoResult result) { |
| 417 if (did_defer_on_will_read_) { |
| 418 DCHECK(has_controller()); |
| 419 DCHECK(!did_defer_on_writing_); |
| 420 DCHECK(!did_defer_on_redirect_); |
| 421 |
| 422 did_defer_on_will_read_ = false; |
| 423 |
| 424 scoped_refptr<net::IOBuffer>* parent_buffer = parent_buffer_; |
| 425 parent_buffer_ = nullptr; |
| 426 int* parent_buffer_size = parent_buffer_size_; |
| 427 parent_buffer_size_ = nullptr; |
| 428 |
| 429 request()->LogUnblocked(); |
| 430 OnWillRead(parent_buffer, parent_buffer_size, ReleaseController()); |
| 431 return; |
| 432 } |
| 433 |
| 434 if (!did_defer_on_writing_) |
| 435 return; |
| 436 DCHECK(has_controller()); |
| 437 DCHECK(!did_defer_on_redirect_); |
| 438 did_defer_on_writing_ = false; |
| 439 |
| 440 DCHECK(is_using_io_buffer_not_from_writer_); |
| 441 // |buffer_| is set to a net::IOBufferWithSize. Write the buffer contents |
| 442 // to the data pipe. |
| 443 DCHECK_GT(buffer_bytes_read_, 0u); |
| 444 if (!CopyReadDataToDataPipe(&did_defer_on_writing_)) { |
| 445 CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
| 446 return; |
| 447 } |
| 448 |
| 449 if (did_defer_on_writing_) { |
| 450 // Continue waiting. |
| 451 return; |
| 452 } |
| 453 request()->LogUnblocked(); |
| 454 Resume(); |
| 455 } |
| 456 |
204 } // namespace content | 457 } // namespace content |
OLD | NEW |