OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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/browser/loader/mojo_async_resource_handler.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/command_line.h" | |
10 #include "base/containers/hash_tables.h" | |
11 #include "base/logging.h" | |
12 #include "base/macros.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "base/time/time.h" | |
15 #include "content/browser/loader/netlog_observer.h" | |
16 #include "content/browser/loader/resource_dispatcher_host_impl.h" | |
17 #include "content/browser/loader/resource_message_filter.h" | |
18 #include "content/browser/loader/resource_request_info_impl.h" | |
19 #include "content/common/resource_request_completion_status.h" | |
20 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
21 #include "content/public/common/resource_response.h" | |
22 #include "mojo/public/c/system/data_pipe.h" | |
23 #include "mojo/public/cpp/system/data_pipe.h" | |
24 #include "net/base/io_buffer.h" | |
25 #include "net/base/load_flags.h" | |
26 #include "net/base/mime_sniffer.h" | |
27 #include "net/log/net_log.h" | |
28 #include "net/url_request/redirect_info.h" | |
29 | |
30 namespace content { | |
31 namespace { | |
32 | |
33 int g_max_allocation_size = 1024 * 32; | |
34 // MimeTypeResourceHandler *implicitly* requires that the buffer size | |
35 // returned from OnWillRead should be larger than certain size. | |
36 // TODO(yhirano): Fix MimeTypeResourceHandler. | |
37 size_t kMinAllocationSize = 2 * net::kMaxBytesToSniff; | |
38 | |
39 void GetNumericArg(const std::string& name, int* result) { | |
40 const std::string& value = | |
41 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name); | |
42 if (!value.empty()) | |
43 base::StringToInt(value, result); | |
44 } | |
45 | |
46 void InitializeResourceBufferConstants() { | |
47 static bool did_init = false; | |
48 if (did_init) | |
49 return; | |
50 did_init = true; | |
51 | |
52 GetNumericArg("resource-buffer-max-allocation-size", &g_max_allocation_size); | |
53 } | |
54 | |
55 } // namespace | |
56 | |
57 class MojoAsyncResourceHandler::SharedWriter final | |
mmenke
2016/06/13 20:56:58
This class needs to be documented.
yhirano
2016/07/12 10:49:11
Done.
| |
58 : public base::RefCountedThreadSafe<SharedWriter> { | |
59 public: | |
60 explicit SharedWriter(mojo::ScopedDataPipeProducerHandle writer) | |
61 : writer_(std::move(writer)) {} | |
62 mojo::DataPipeProducerHandle writer() { return writer_.get(); } | |
63 | |
64 private: | |
65 friend class base::RefCountedThreadSafe<SharedWriter>; | |
66 ~SharedWriter() {} | |
67 | |
68 const mojo::ScopedDataPipeProducerHandle writer_; | |
69 | |
70 DISALLOW_COPY_AND_ASSIGN(SharedWriter); | |
71 }; | |
72 | |
73 class MojoAsyncResourceHandler::WriterIOBuffer final | |
mmenke
2016/06/13 20:56:58
This class needs to be documented.
yhirano
2016/07/12 10:49:11
Done.
| |
74 : public net::IOBufferWithSize { | |
75 public: | |
76 WriterIOBuffer(scoped_refptr<MojoAsyncResourceHandler::SharedWriter> writer, | |
77 void* data, | |
78 size_t size) | |
79 : net::IOBufferWithSize(static_cast<char*>(data), size), | |
80 writer_(std::move(writer)) {} | |
81 | |
82 private: | |
83 ~WriterIOBuffer() override { | |
84 // Avoid deleting |data_| in the IOBuffer destructor. | |
85 data_ = nullptr; | |
86 } | |
87 | |
88 // This member is for keeping the writer alive. | |
89 scoped_refptr<MojoAsyncResourceHandler::SharedWriter> writer_; | |
90 | |
91 DISALLOW_COPY_AND_ASSIGN(WriterIOBuffer); | |
92 }; | |
93 | |
94 MojoAsyncResourceHandler::MojoAsyncResourceHandler( | |
95 net::URLRequest* request, | |
96 ResourceDispatcherHostImpl* rdh, | |
97 std::unique_ptr<mojom::URLLoader> url_loader, | |
98 mojom::URLLoaderClientPtr url_loader_client) | |
99 : ResourceHandler(request), | |
100 rdh_(rdh), | |
101 url_loader_(std::move(url_loader)), | |
102 url_loader_client_(std::move(url_loader_client)) { | |
103 DCHECK(url_loader_); | |
104 DCHECK(url_loader_client_); | |
105 InitializeResourceBufferConstants(); | |
106 } | |
107 | |
108 MojoAsyncResourceHandler::~MojoAsyncResourceHandler() { | |
109 if (has_checked_for_sufficient_resources_) | |
110 rdh_->FinishedWithResourcesForRequest(request()); | |
111 } | |
112 | |
113 bool MojoAsyncResourceHandler::OnRequestRedirected( | |
114 const net::RedirectInfo& redirect_info, | |
115 ResourceResponse* response, | |
116 bool* defer) { | |
117 // Not implemented. | |
118 return false; | |
119 } | |
120 | |
121 bool MojoAsyncResourceHandler::OnResponseStarted(ResourceResponse* response, | |
122 bool* defer) { | |
123 const ResourceRequestInfoImpl* info = GetRequestInfo(); | |
124 if (!info->filter()) | |
125 return false; | |
126 | |
127 if (rdh_->delegate()) { | |
128 rdh_->delegate()->OnResponseStarted(request(), info->GetContext(), response, | |
129 info->filter()); | |
130 } | |
131 | |
132 NetLogObserver::PopulateResponseInfo(request(), response); | |
133 | |
134 response->head.request_start = request()->creation_time(); | |
135 response->head.response_start = base::TimeTicks::Now(); | |
136 sent_received_response_message_ = true; | |
137 url_loader_client_->OnReceiveResponse(response->head); | |
138 return true; | |
139 } | |
140 | |
141 bool MojoAsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) { | |
142 return true; | |
143 } | |
144 | |
145 bool MojoAsyncResourceHandler::OnBeforeNetworkStart(const GURL& url, | |
146 bool* defer) { | |
147 return true; | |
148 } | |
149 | |
150 bool MojoAsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, | |
151 int* buf_size, | |
152 int min_size) { | |
153 DCHECK_EQ(-1, min_size); | |
154 | |
155 if (!CheckForSufficientResource()) | |
156 return false; | |
157 | |
158 if (!shared_writer_) { | |
159 MojoCreateDataPipeOptions options; | |
160 options.struct_size = sizeof(MojoCreateDataPipeOptions); | |
161 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; | |
162 options.element_num_bytes = 1; | |
163 options.capacity_num_bytes = g_max_allocation_size; | |
164 mojo::DataPipe data_pipe(options); | |
165 | |
166 ResourceMessageFilter* filter = GetRequestInfo()->filter(); | |
167 if (filter) { | |
mmenke
2016/06/13 20:56:58
All these filter checks, except possibly the one i
yhirano
2016/07/12 10:49:11
I tried to mimic AsyncResourceHandler's behavior,
mmenke
2016/07/12 15:10:45
My opinion here (And it's certainly something that
| |
168 url_loader_client_->OnStartLoadingResponseBody( | |
169 std::move(data_pipe.consumer_handle)); | |
170 } | |
171 if (!data_pipe.producer_handle.is_valid()) | |
172 return false; | |
173 | |
174 shared_writer_ = new SharedWriter(std::move(data_pipe.producer_handle)); | |
175 | |
176 void* data = nullptr; | |
177 uint32_t available = 0; | |
178 MojoResult result = mojo::BeginWriteDataRaw( | |
179 shared_writer_->writer(), &data, &available, MOJO_WRITE_DATA_FLAG_NONE); | |
180 | |
181 if (result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT) | |
182 return false; | |
183 if (result == MOJO_RESULT_OK && available >= kMinAllocationSize) { | |
184 *buf = buffer_ = new WriterIOBuffer(shared_writer_, data, available); | |
185 *buf_size = buffer_->size(); | |
186 return true; | |
187 } | |
188 | |
189 if (result == MOJO_RESULT_OK) { | |
190 // The allocated buffer is too small. | |
191 if (mojo::EndWriteDataRaw(shared_writer_->writer(), 0) != MOJO_RESULT_OK) | |
192 return false; | |
193 } | |
194 DCHECK(!is_using_io_buffer_not_from_writer_); | |
195 is_using_io_buffer_not_from_writer_ = true; | |
196 buffer_ = new net::IOBufferWithSize(kMinAllocationSize); | |
197 } | |
198 | |
199 if (is_using_io_buffer_not_from_writer_) { | |
200 DCHECK_EQ(0u, buffer_offset_); | |
201 *buf = new net::WrappedIOBuffer(buffer_->data()); | |
202 *buf_size = buffer_->size(); | |
203 return true; | |
204 } | |
205 | |
206 *buf = buffer_; | |
207 *buf_size = buffer_->size(); | |
208 return true; | |
209 } | |
210 | |
211 bool MojoAsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { | |
212 DCHECK_GE(bytes_read, 0); | |
213 DCHECK(buffer_); | |
214 | |
215 if (!bytes_read) | |
216 return true; | |
217 | |
218 if (is_using_io_buffer_not_from_writer_) { | |
219 // Couldn't allocate a buffer on the data pipe in OnWillRead. | |
220 DCHECK(!did_defer_); | |
221 DCHECK_EQ(0u, buffer_bytes_read_); | |
222 buffer_bytes_read_ = bytes_read; | |
223 if (!CopyReadData(defer)) | |
224 return false; | |
225 if (*defer) { | |
226 did_defer_ = true; | |
227 OnDefer(); | |
228 } | |
229 return true; | |
230 } | |
231 | |
232 MojoResult result = | |
233 mojo::EndWriteDataRaw(shared_writer_->writer(), bytes_read); | |
234 if (result != MOJO_RESULT_OK) | |
235 return false; | |
236 if (!AllocateBuffer(defer)) | |
237 return false; | |
238 if (*defer) { | |
239 did_defer_ = true; | |
240 OnDefer(); | |
241 } | |
242 return true; | |
243 } | |
244 | |
245 void MojoAsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { | |
246 // Not implemented. | |
247 } | |
248 | |
249 void MojoAsyncResourceHandler::SetAllocationSizeForTesting(size_t size) { | |
250 g_max_allocation_size = size; | |
251 } | |
252 | |
253 void MojoAsyncResourceHandler::OnResponseCompleted( | |
254 const net::URLRequestStatus& status, | |
255 const std::string& security_info, | |
256 bool* defer) { | |
257 const ResourceRequestInfoImpl* info = GetRequestInfo(); | |
258 if (!info->filter()) | |
259 return; | |
260 | |
261 // TODO(gavinp): Remove this CHECK when we figure out the cause of | |
262 // http://crbug.com/124680 . This check mirrors closely check in | |
263 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore | |
264 // ResourceHandleInternal which asserts on its state and crashes. By crashing | |
265 // when the message is sent, we should get better crash reports. | |
266 CHECK(status.status() != net::URLRequestStatus::SUCCESS || | |
267 sent_received_response_message_); | |
268 | |
269 int error_code = status.error(); | |
270 bool was_ignored_by_handler = info->WasIgnoredByHandler(); | |
271 | |
272 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING); | |
273 // If this check fails, then we're in an inconsistent state because all | |
274 // requests ignored by the handler should be canceled (which should result in | |
275 // the ERR_ABORTED error code). | |
276 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED); | |
277 | |
278 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus | |
279 // with a status() != SUCCESS and an error_code() == net::OK. | |
280 if (status.status() == net::URLRequestStatus::CANCELED && | |
281 error_code == net::OK) { | |
282 error_code = net::ERR_ABORTED; | |
283 } else if (status.status() == net::URLRequestStatus::FAILED && | |
284 error_code == net::OK) { | |
285 error_code = net::ERR_FAILED; | |
286 } | |
287 | |
288 ResourceRequestCompletionStatus request_complete_data; | |
289 request_complete_data.error_code = error_code; | |
290 request_complete_data.was_ignored_by_handler = was_ignored_by_handler; | |
291 request_complete_data.exists_in_cache = request()->response_info().was_cached; | |
292 request_complete_data.security_info = security_info; | |
293 request_complete_data.completion_time = base::TimeTicks::Now(); | |
294 request_complete_data.encoded_data_length = | |
295 request()->GetTotalReceivedBytes(); | |
296 | |
297 url_loader_client_->OnComplete(request_complete_data); | |
298 } | |
299 | |
300 bool MojoAsyncResourceHandler::CopyReadData(bool* defer) { | |
301 while (true) { | |
302 void* data = nullptr; | |
303 uint32_t available = 0; | |
304 MojoResult result = mojo::BeginWriteDataRaw( | |
305 shared_writer_->writer(), &data, &available, MOJO_WRITE_DATA_FLAG_NONE); | |
306 if (result == MOJO_RESULT_SHOULD_WAIT) { | |
307 *defer = true; | |
308 return true; | |
309 } | |
310 if (result != MOJO_RESULT_OK) | |
311 return false; | |
312 | |
313 if (buffer_bytes_read_ == 0) { | |
314 buffer_ = new WriterIOBuffer(shared_writer_, data, available); | |
315 return true; | |
316 } | |
317 | |
318 size_t copied_size = | |
319 std::min(static_cast<uint32_t>(buffer_bytes_read_), available); | |
320 memcpy(data, buffer_->data() + buffer_offset_, copied_size); | |
321 buffer_offset_ += copied_size; | |
322 buffer_bytes_read_ -= copied_size; | |
323 result = mojo::EndWriteDataRaw(shared_writer_->writer(), copied_size); | |
324 if (result != MOJO_RESULT_OK) | |
325 return false; | |
326 if (buffer_bytes_read_ == 0) { | |
mmenke
2016/06/13 20:56:58
I think it's clearer (And less code) if you move t
yhirano
2016/07/12 10:49:11
Sorry I don't understand. Do you mean "the start o
mmenke
2016/07/12 15:10:45
Yes
yhirano
2016/07/13 12:04:57
Done.
| |
327 // All bytes are copied. | |
328 buffer_offset_ = 0; | |
329 is_using_io_buffer_not_from_writer_ = false; | |
330 } | |
331 } | |
332 NOTREACHED(); | |
333 return false; | |
mmenke
2016/06/13 20:56:58
There's no break in the loop. These two lines sho
yhirano
2016/07/12 10:49:11
Done.
| |
334 } | |
335 | |
336 bool MojoAsyncResourceHandler::AllocateBuffer(bool* defer) { | |
337 void* data = nullptr; | |
338 uint32_t available = 0; | |
339 MojoResult result = mojo::BeginWriteDataRaw( | |
340 shared_writer_->writer(), &data, &available, MOJO_WRITE_DATA_FLAG_NONE); | |
341 if (result == MOJO_RESULT_SHOULD_WAIT) { | |
342 *defer = true; | |
343 return true; | |
344 } | |
345 if (result != MOJO_RESULT_OK) | |
346 return false; | |
347 buffer_ = new WriterIOBuffer(shared_writer_, data, available); | |
348 return true; | |
349 } | |
350 | |
351 void MojoAsyncResourceHandler::ResumeIfDeferred() { | |
352 if (did_defer_) { | |
mmenke
2016/06/13 20:56:58
Can this ever be called if did_defer_ is false?
yhirano
2016/07/12 10:49:11
Currently not.
| |
353 // did_defer_ is set only in OnReadCompleted (and here). | |
354 did_defer_ = false; | |
355 if (is_using_io_buffer_not_from_writer_) { | |
356 DCHECK_GT(buffer_bytes_read_, 0u); | |
357 if (!CopyReadData(&did_defer_)) { | |
358 controller()->CancelWithError(net::ERR_FAILED); | |
359 return; | |
360 } | |
361 } else { | |
362 if (!AllocateBuffer(&did_defer_)) { | |
363 controller()->CancelWithError(net::ERR_FAILED); | |
364 return; | |
365 } | |
366 } | |
367 if (did_defer_) { | |
368 // Continue waiting. | |
369 handle_watcher_.Start(shared_writer_->writer(), | |
370 MOJO_HANDLE_SIGNAL_WRITABLE, | |
371 MOJO_DEADLINE_INDEFINITE, | |
372 base::Bind(&MojoAsyncResourceHandler::OnWritable, | |
373 base::Unretained(this))); | |
374 return; | |
375 } | |
376 request()->LogUnblocked(); | |
377 controller()->Resume(); | |
378 } | |
379 } | |
380 | |
381 void MojoAsyncResourceHandler::OnDefer() { | |
382 request()->LogBlockedBy("MojoAsyncResourceHandler"); | |
383 handle_watcher_.Start(shared_writer_->writer(), MOJO_HANDLE_SIGNAL_WRITABLE, | |
384 MOJO_DEADLINE_INDEFINITE, | |
385 base::Bind(&MojoAsyncResourceHandler::OnWritable, | |
386 base::Unretained(this))); | |
387 } | |
388 | |
389 bool MojoAsyncResourceHandler::CheckForSufficientResource() { | |
390 if (has_checked_for_sufficient_resources_) | |
391 return true; | |
392 has_checked_for_sufficient_resources_ = true; | |
393 | |
394 if (rdh_->HasSufficientResourcesForRequest(request())) | |
395 return true; | |
396 | |
397 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); | |
398 return false; | |
399 } | |
400 | |
401 void MojoAsyncResourceHandler::OnWritable(MojoResult unused) { | |
402 ResumeIfDeferred(); | |
403 } | |
404 | |
405 } // namespace content | |
OLD | NEW |