Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(847)

Side by Side Diff: content/browser/loader/mojo_async_resource_handler.cc

Issue 1970693002: Use mojo for Chrome Loading, Part 1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698