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() | |
181 .WithPriority(base::TaskPriority::USER_VISIBLE)); | |
182 url_request_->set_upload(CreateUploadDataStream( | |
183 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 // TODO | |
yzshen1
2017/04/11 21:58:04
Please consider adding NOTIMPLEMENTED() and/or des
jam
2017/04/11 22:58:19
Done.
| |
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 response_body_consumer_handle_.reset(); | |
yzshen1
2017/04/11 21:58:04
nit: no need to reset, it is already reset by the
jam
2017/04/11 22:58:19
Done.
| |
365 } | |
366 } | |
367 | |
368 void URLLoaderImpl::OnConnectionError() { | |
369 connected_ = false; | |
370 DeleteIfNeeded(); | |
371 } | |
372 | |
373 void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) { | |
374 url_request_.reset(); | |
375 response_body_stream_.reset(); | |
376 pending_write_ = nullptr; | |
377 DeleteIfNeeded(); | |
378 } | |
379 | |
380 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) { | |
381 // TODO: Handle a bad |result| value. | |
382 DCHECK_EQ(result, MOJO_RESULT_OK); | |
383 ReadMore(); | |
384 } | |
385 | |
386 void URLLoaderImpl::DeleteIfNeeded() { | |
387 bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid(); | |
388 if (!connected_ && !has_data_pipe) | |
389 delete this; | |
390 } | |
391 | |
392 } // namespace content | |
OLD | NEW |