OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "mojo/services/network/url_loader_impl.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <stdint.h> | |
9 | |
10 #include <utility> | |
11 #include <vector> | |
12 | |
13 #include "base/macros.h" | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/message_loop/message_loop.h" | |
16 #include "mojo/common/common_type_converters.h" | |
17 #include "mojo/common/url_type_converters.h" | |
18 #include "mojo/services/network/net_adapters.h" | |
19 #include "mojo/services/network/network_context.h" | |
20 #include "net/base/elements_upload_data_stream.h" | |
21 #include "net/base/io_buffer.h" | |
22 #include "net/base/load_flags.h" | |
23 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
24 #include "net/base/upload_bytes_element_reader.h" | |
25 #include "net/http/http_response_headers.h" | |
26 #include "net/url_request/redirect_info.h" | |
27 #include "net/url_request/url_request_context.h" | |
28 | |
29 namespace mojo { | |
30 namespace { | |
31 | |
32 GURL GetSiteForURL(const GURL& url) { | |
33 // If the url has a host, then determine the site. | |
34 if (url.has_host()) { | |
35 // Only keep the scheme and registered domain as given by GetOrigin. This | |
36 // may also include a port, which we need to drop. | |
37 GURL site = url.GetOrigin(); | |
38 | |
39 // Remove port, if any. | |
40 if (site.has_port()) { | |
41 GURL::Replacements rep; | |
42 rep.ClearPort(); | |
43 site = site.ReplaceComponents(rep); | |
44 } | |
45 | |
46 // If this URL has a registered domain, we only want to remember that part. | |
47 std::string domain = net::registry_controlled_domains::GetDomainAndRegistry( | |
48 url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | |
49 if (!domain.empty()) { | |
50 GURL::Replacements rep; | |
51 rep.SetHostStr(domain); | |
52 site = site.ReplaceComponents(rep); | |
53 } | |
54 return site; | |
55 } | |
56 | |
57 // If there is no host but there is a scheme, return the scheme. | |
58 // This is useful for cases like file URLs. | |
59 if (url.has_scheme()) | |
60 return GURL(url.scheme() + ":"); | |
61 | |
62 // Otherwise the URL should be invalid; return an empty site. | |
63 DCHECK(!url.is_valid()); | |
64 return GURL(); | |
65 } | |
66 | |
67 // Generates an URLResponsePtr from the response state of a net::URLRequest. | |
68 URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) { | |
69 URLResponsePtr response(URLResponse::New()); | |
70 response->url = String::From(url_request->url()); | |
71 response->site = GetSiteForURL(url_request->url()).spec(); | |
72 | |
73 const net::HttpResponseHeaders* headers = url_request->response_headers(); | |
74 if (headers) { | |
75 response->status_code = headers->response_code(); | |
76 response->status_line = headers->GetStatusLine(); | |
77 | |
78 response->headers = Array<HttpHeaderPtr>::New(0); | |
79 std::vector<String> header_lines; | |
80 size_t iter = 0; | |
81 std::string name, value; | |
82 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
83 HttpHeaderPtr header = HttpHeader::New(); | |
84 header->name = name; | |
85 header->value = value; | |
86 response->headers.push_back(std::move(header)); | |
87 } | |
88 } | |
89 | |
90 std::string mime_type; | |
91 url_request->GetMimeType(&mime_type); | |
92 response->mime_type = mime_type; | |
93 | |
94 std::string charset; | |
95 url_request->GetCharset(&charset); | |
96 response->charset = charset; | |
97 | |
98 return response; | |
99 } | |
100 | |
101 // Reads the request body upload data from a DataPipe. | |
102 class UploadDataPipeElementReader : public net::UploadElementReader { | |
103 public: | |
104 UploadDataPipeElementReader(ScopedDataPipeConsumerHandle pipe) | |
105 : pipe_(std::move(pipe)), num_bytes_(0) {} | |
106 ~UploadDataPipeElementReader() override {} | |
107 | |
108 // UploadElementReader overrides: | |
109 int Init(const net::CompletionCallback& callback) override { | |
110 offset_ = 0; | |
111 ReadDataRaw(pipe_.get(), nullptr, &num_bytes_, MOJO_READ_DATA_FLAG_QUERY); | |
112 return net::OK; | |
113 } | |
114 uint64_t GetContentLength() const override { return num_bytes_; } | |
115 uint64_t BytesRemaining() const override { return num_bytes_ - offset_; } | |
116 bool IsInMemory() const override { return false; } | |
117 int Read(net::IOBuffer* buf, | |
118 int buf_length, | |
119 const net::CompletionCallback& callback) override { | |
120 uint32_t bytes_read = | |
121 std::min(static_cast<uint32_t>(BytesRemaining()), | |
122 static_cast<uint32_t>(buf_length)); | |
123 if (bytes_read > 0) { | |
124 ReadDataRaw(pipe_.get(), buf->data(), &bytes_read, | |
125 MOJO_READ_DATA_FLAG_NONE); | |
126 } | |
127 | |
128 offset_ += bytes_read; | |
129 return bytes_read; | |
130 } | |
131 | |
132 private: | |
133 ScopedDataPipeConsumerHandle pipe_; | |
134 uint32_t num_bytes_; | |
135 uint32_t offset_; | |
136 | |
137 DISALLOW_COPY_AND_ASSIGN(UploadDataPipeElementReader); | |
138 }; | |
139 | |
140 } // namespace | |
141 | |
142 URLLoaderImpl::URLLoaderImpl(NetworkContext* context, | |
143 InterfaceRequest<URLLoader> request, | |
144 scoped_ptr<mojo::MessageLoopRef> app_refcount) | |
145 : context_(context), | |
146 response_body_buffer_size_(0), | |
147 response_body_bytes_read_(0), | |
148 auto_follow_redirects_(true), | |
149 connected_(true), | |
150 binding_(this, std::move(request)), | |
151 app_refcount_(std::move(app_refcount)), | |
152 weak_ptr_factory_(this) { | |
153 binding_.set_connection_error_handler([this]() { OnConnectionError(); }); | |
154 context_->RegisterURLLoader(this); | |
155 } | |
156 | |
157 URLLoaderImpl::~URLLoaderImpl() { | |
158 context_->DeregisterURLLoader(this); | |
159 } | |
160 | |
161 void URLLoaderImpl::Cleanup() { | |
162 // The associated network context is going away and we have to destroy | |
163 // net::URLRequest hold by this loader. | |
164 delete this; | |
165 } | |
166 | |
167 void URLLoaderImpl::Start(URLRequestPtr request, | |
168 const Callback<void(URLResponsePtr)>& callback) { | |
169 if (url_request_) { | |
170 SendError(net::ERR_UNEXPECTED, callback); | |
171 return; | |
172 } | |
173 | |
174 if (!request) { | |
175 SendError(net::ERR_INVALID_ARGUMENT, callback); | |
176 return; | |
177 } | |
178 | |
179 url_request_ = context_->url_request_context()->CreateRequest( | |
180 GURL(request->url.get()), net::DEFAULT_PRIORITY, this); | |
181 url_request_->set_method(request->method); | |
182 // TODO(jam): need to specify this policy. | |
183 url_request_->set_referrer_policy( | |
184 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE); | |
185 if (request->headers) { | |
186 net::HttpRequestHeaders headers; | |
187 for (size_t i = 0; i < request->headers.size(); ++i) { | |
188 base::StringPiece header = | |
189 request->headers[i]->name.To<base::StringPiece>(); | |
190 base::StringPiece value = | |
191 request->headers[i]->value.To<base::StringPiece>(); | |
192 if (header == net::HttpRequestHeaders::kReferer) { | |
193 url_request_->SetReferrer(value.as_string()); | |
194 } else { | |
195 headers.SetHeader(header, value); | |
196 } | |
197 } | |
198 url_request_->SetExtraRequestHeaders(headers); | |
199 } | |
200 if (request->body) { | |
201 std::vector<scoped_ptr<net::UploadElementReader>> element_readers; | |
202 for (size_t i = 0; i < request->body.size(); ++i) { | |
203 element_readers.push_back(make_scoped_ptr( | |
204 new UploadDataPipeElementReader(std::move(request->body[i])))); | |
205 } | |
206 url_request_->set_upload(make_scoped_ptr<net::UploadDataStream>( | |
207 new net::ElementsUploadDataStream(std::move(element_readers), 0))); | |
208 } | |
209 if (request->bypass_cache) | |
210 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE); | |
211 | |
212 callback_ = callback; | |
213 response_body_buffer_size_ = request->response_body_buffer_size; | |
214 auto_follow_redirects_ = request->auto_follow_redirects; | |
215 | |
216 url_request_->Start(); | |
217 } | |
218 | |
219 void URLLoaderImpl::FollowRedirect( | |
220 const Callback<void(URLResponsePtr)>& callback) { | |
221 if (!url_request_) { | |
222 SendError(net::ERR_UNEXPECTED, callback); | |
223 return; | |
224 } | |
225 | |
226 if (auto_follow_redirects_) { | |
227 DLOG(ERROR) << "Spurious call to FollowRedirect"; | |
228 SendError(net::ERR_UNEXPECTED, callback); | |
229 return; | |
230 } | |
231 | |
232 // TODO(darin): Verify that it makes sense to call FollowDeferredRedirect. | |
233 url_request_->FollowDeferredRedirect(); | |
234 callback_ = callback; | |
235 } | |
236 | |
237 void URLLoaderImpl::QueryStatus( | |
238 const Callback<void(URLLoaderStatusPtr)>& callback) { | |
239 URLLoaderStatusPtr status(URLLoaderStatus::New()); | |
240 status->bytes_read = response_body_bytes_read_; | |
241 if (url_request_) { | |
242 status->is_loading = url_request_->is_pending(); | |
243 if (!url_request_->status().is_success()) | |
244 status->error = MakeNetworkError(url_request_->status().error()); | |
245 if (url_request_->response_info().headers) { | |
246 status->content_length = | |
247 url_request_->response_info().headers->GetContentLength(); | |
248 } | |
249 } else { | |
250 status->is_loading = false; | |
251 } | |
252 // TODO(darin): Populate more status fields. | |
253 callback.Run(std::move(status)); | |
254 } | |
255 | |
256 void URLLoaderImpl::OnConnectionError() { | |
257 connected_ = false; | |
258 DeleteIfNeeded(); | |
259 } | |
260 | |
261 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request, | |
262 const net::RedirectInfo& redirect_info, | |
263 bool* defer_redirect) { | |
264 DCHECK(url_request == url_request_.get()); | |
265 DCHECK(url_request->status().is_success()); | |
266 | |
267 if (auto_follow_redirects_) | |
268 return; | |
269 | |
270 // Send the redirect response to the client, allowing them to inspect it and | |
271 // optionally follow the redirect. | |
272 *defer_redirect = true; | |
273 | |
274 URLResponsePtr response = MakeURLResponse(url_request); | |
275 response->redirect_method = redirect_info.new_method; | |
276 response->redirect_url = String::From(redirect_info.new_url); | |
277 response->redirect_referrer = redirect_info.new_referrer; | |
278 | |
279 SendResponse(std::move(response)); | |
280 | |
281 DeleteIfNeeded(); | |
282 } | |
283 | |
284 void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) { | |
285 DCHECK(url_request == url_request_.get()); | |
286 | |
287 if (!url_request->status().is_success()) { | |
288 SendError(url_request->status().error(), callback_); | |
289 callback_ = Callback<void(URLResponsePtr)>(); | |
290 DeleteIfNeeded(); | |
291 return; | |
292 } | |
293 | |
294 // TODO(darin): Add support for optional MIME sniffing. | |
295 | |
296 DataPipe data_pipe; | |
297 // TODO(darin): Honor given buffer size. | |
298 | |
299 URLResponsePtr response = MakeURLResponse(url_request); | |
300 response->body = std::move(data_pipe.consumer_handle); | |
301 response_body_stream_ = std::move(data_pipe.producer_handle); | |
302 ListenForPeerClosed(); | |
303 | |
304 SendResponse(std::move(response)); | |
305 | |
306 // Start reading... | |
307 ReadMore(); | |
308 } | |
309 | |
310 void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request, | |
311 int bytes_read) { | |
312 DCHECK(url_request == url_request_.get()); | |
313 | |
314 if (url_request->status().is_success()) { | |
315 DidRead(static_cast<uint32_t>(bytes_read), false); | |
316 } else { | |
317 handle_watcher_.Stop(); | |
318 pending_write_ = nullptr; // This closes the data pipe. | |
319 DeleteIfNeeded(); | |
320 return; | |
321 } | |
322 } | |
323 | |
324 void URLLoaderImpl::SendError( | |
325 int error_code, | |
326 const Callback<void(URLResponsePtr)>& callback) { | |
327 URLResponsePtr response(URLResponse::New()); | |
328 if (url_request_) | |
329 response->url = String::From(url_request_->url()); | |
330 response->error = MakeNetworkError(error_code); | |
331 callback.Run(std::move(response)); | |
332 } | |
333 | |
334 void URLLoaderImpl::SendResponse(URLResponsePtr response) { | |
335 Callback<void(URLResponsePtr)> callback; | |
336 std::swap(callback_, callback); | |
337 callback.Run(std::move(response)); | |
338 } | |
339 | |
340 void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) { | |
341 // TODO(darin): Handle a bad |result| value. | |
342 | |
343 // Continue watching the handle in case the peer is closed. | |
344 ListenForPeerClosed(); | |
345 ReadMore(); | |
346 } | |
347 | |
348 void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) { | |
349 url_request_.reset(); | |
350 response_body_stream_.reset(); | |
351 pending_write_ = nullptr; | |
352 DeleteIfNeeded(); | |
353 } | |
354 | |
355 void URLLoaderImpl::ReadMore() { | |
356 DCHECK(!pending_write_.get()); | |
357 | |
358 uint32_t num_bytes; | |
359 MojoResult result = NetToMojoPendingBuffer::BeginWrite( | |
360 &response_body_stream_, &pending_write_, &num_bytes); | |
361 | |
362 if (result == MOJO_RESULT_SHOULD_WAIT) { | |
363 // The pipe is full. We need to wait for it to have more space. | |
364 handle_watcher_.Start(response_body_stream_.get(), | |
365 MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, | |
366 base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady, | |
367 base::Unretained(this))); | |
368 return; | |
369 } else if (result != MOJO_RESULT_OK) { | |
370 // The response body stream is in a bad state. Bail. | |
371 // TODO(darin): How should this be communicated to our client? | |
372 handle_watcher_.Stop(); | |
373 response_body_stream_.reset(); | |
374 DeleteIfNeeded(); | |
375 return; | |
376 } | |
377 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes); | |
378 | |
379 scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(pending_write_.get())); | |
380 | |
381 int bytes_read; | |
382 url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read); | |
383 if (url_request_->status().is_io_pending()) { | |
384 // Wait for OnReadCompleted. | |
385 } else if (url_request_->status().is_success() && bytes_read > 0) { | |
386 DidRead(static_cast<uint32_t>(bytes_read), true); | |
387 } else { | |
388 handle_watcher_.Stop(); | |
389 pending_write_->Complete(0); | |
390 pending_write_ = nullptr; // This closes the data pipe. | |
391 DeleteIfNeeded(); | |
392 return; | |
393 } | |
394 } | |
395 | |
396 void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) { | |
397 DCHECK(url_request_->status().is_success()); | |
398 | |
399 response_body_bytes_read_ += num_bytes; | |
400 response_body_stream_ = pending_write_->Complete(num_bytes); | |
401 pending_write_ = nullptr; | |
402 | |
403 if (completed_synchronously) { | |
404 base::MessageLoop::current()->PostTask( | |
405 FROM_HERE, | |
406 base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr())); | |
407 } else { | |
408 ReadMore(); | |
409 } | |
410 } | |
411 | |
412 void URLLoaderImpl::DeleteIfNeeded() { | |
413 bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid(); | |
414 if (!connected_ && !has_data_pipe) | |
415 delete this; | |
416 } | |
417 | |
418 void URLLoaderImpl::ListenForPeerClosed() { | |
419 handle_watcher_.Start(response_body_stream_.get(), | |
420 MOJO_HANDLE_SIGNAL_PEER_CLOSED, | |
421 MOJO_DEADLINE_INDEFINITE, | |
422 base::Bind(&URLLoaderImpl::OnResponseBodyStreamClosed, | |
423 base::Unretained(this))); | |
424 } | |
425 | |
426 } // namespace mojo | |
OLD | NEW |