| Index: chrome/browser/extensions/api/dial/device_description_fetcher.cc
|
| diff --git a/chrome/browser/extensions/api/dial/device_description_fetcher.cc b/chrome/browser/extensions/api/dial/device_description_fetcher.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7fe59e542b9950b5ff447aaf83b54a214b05b154
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/api/dial/device_description_fetcher.cc
|
| @@ -0,0 +1,136 @@
|
| +// Copyright (c) 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/strings/string_util.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "chrome/browser/extensions/api/dial/device_description_fetcher.h"
|
| +#include "chrome/browser/extensions/api/dial/dial_device_data.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "net/base/load_flags.h"
|
| +#include "net/http/http_response_headers.h"
|
| +#include "net/http/http_status_code.h"
|
| +#include "net/http/http_util.h"
|
| +#include "net/url_request/url_fetcher.h"
|
| +
|
| +constexpr char kApplicationUrlHeaderName[] = "Application-URL";
|
| +constexpr int kMaxRetries = 3;
|
| +constexpr int kMaxDescriptionSizeBytes = 262144;
|
| +
|
| +namespace extensions {
|
| +
|
| +DeviceDescriptionFetcher::DeviceDescriptionFetcher(
|
| + const GURL& device_description_url,
|
| + Profile* profile,
|
| + base::OnceCallback<void(const DialDeviceDescription&)> success_cb,
|
| + base::OnceCallback<void(const std::string&)> error_cb)
|
| + : device_description_url_(device_description_url),
|
| + profile_(profile),
|
| + success_cb_(std::move(success_cb)),
|
| + error_cb_(std::move(error_cb)) {
|
| + DCHECK(profile_);
|
| + DCHECK(device_description_url_.is_valid());
|
| +}
|
| +
|
| +DeviceDescriptionFetcher::~DeviceDescriptionFetcher() {}
|
| +
|
| +void DeviceDescriptionFetcher::Start() {
|
| + DCHECK(!fetcher_);
|
| + fetcher_ = net::URLFetcher::Create(kURLFetcherID, device_description_url_,
|
| + net::URLFetcher::GET, this);
|
| +
|
| + // The request should not side effect cache or the cookie store or send auth
|
| + // data. Proxies almost certainly hurt more cases than they help.
|
| + fetcher_->SetLoadFlags(net::LOAD_BYPASS_PROXY | net::LOAD_DISABLE_CACHE |
|
| + net::LOAD_DO_NOT_SAVE_COOKIES |
|
| + net::LOAD_DO_NOT_SEND_COOKIES |
|
| + net::LOAD_DO_NOT_SEND_AUTH_DATA);
|
| +
|
| + // Technically uPnP allows devices to issue HTTP 307 redirects (sec
|
| + // 2.1). However, we want to enforce that redirects are not followed to hosts
|
| + // other than the DIAL device; and there is no logical reason why a device
|
| + // needs to redirect a request for the device description.
|
| + //
|
| + // We can relax this restriction if there are well-behaved devices that
|
| + // actually need redirects.
|
| + fetcher_->SetStopOnRedirect(true);
|
| +
|
| + // Allow the fetcher to retry on 5XX responses and ERR_NETWORK_CHANGED.
|
| + fetcher_->SetMaxRetriesOn5xx(kMaxRetries);
|
| + fetcher_->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
|
| +
|
| + fetcher_->SetRequestContext(profile_->GetRequestContext());
|
| + fetcher_->Start();
|
| +}
|
| +
|
| +void DeviceDescriptionFetcher::OnURLFetchComplete(
|
| + const net::URLFetcher* source) {
|
| + if (source->GetResponseCode() != net::HTTP_OK) {
|
| + ReportError(
|
| + base::StringPrintf("HTTP %d: Unable to fetch device description",
|
| + source->GetResponseCode()));
|
| + return;
|
| + }
|
| +
|
| + const net::HttpResponseHeaders* headers = source->GetResponseHeaders();
|
| +
|
| + // NOTE: The uPnP spec requires devices to set a Content-Type: header of
|
| + // text/xml; charset="utf-8" (sec 2.11). However Chromecast (and possibly
|
| + // other devices) do not comply, so not checking this header specifcially.
|
| +
|
| + std::string app_url_header;
|
| + if (!headers->GetNormalizedHeader(kApplicationUrlHeaderName,
|
| + &app_url_header) ||
|
| + app_url_header.empty()) {
|
| + ReportError("Missing or empty Application-URL:");
|
| + return;
|
| + }
|
| +
|
| + // Section 5.4 of the DIAL spec implies that the Application URL should not
|
| + // have path, query or fragment...unsure if that can be enforced.
|
| + GURL app_url(app_url_header);
|
| + if (!app_url.is_valid() || !app_url.SchemeIs("http") ||
|
| + !app_url.HostIsIPAddress() ||
|
| + app_url.host() != device_description_url_.host()) {
|
| + ReportError(base::StringPrintf("Invalid Application-URL: %s",
|
| + app_url_header.c_str()));
|
| + return;
|
| + }
|
| +
|
| + if (source->GetReceivedResponseContentLength() > kMaxDescriptionSizeBytes) {
|
| + ReportError("Response too large");
|
| + return;
|
| + }
|
| +
|
| + std::string device_description;
|
| + if (!source->GetResponseAsString(&device_description) ||
|
| + device_description.empty()) {
|
| + ReportError("Missing or empty response");
|
| + return;
|
| + }
|
| +
|
| + if (!base::IsStringUTF8(device_description)) {
|
| + ReportError("Invalid response encoding");
|
| + return;
|
| + }
|
| +
|
| + std::move(success_cb_)
|
| + .Run(DialDeviceDescription(device_description, app_url));
|
| +}
|
| +
|
| +void DeviceDescriptionFetcher::OnURLFetchDownloadProgress(
|
| + const net::URLFetcher* source,
|
| + int64_t current,
|
| + int64_t total,
|
| + int64_t current_network_bytes) {}
|
| +
|
| +void DeviceDescriptionFetcher::OnURLFetchUploadProgress(
|
| + const net::URLFetcher* source,
|
| + int64_t current,
|
| + int64_t total) {}
|
| +
|
| +void DeviceDescriptionFetcher::ReportError(const std::string& message) {
|
| + std::move(error_cb_).Run(message);
|
| +}
|
| +
|
| +} // namespace extensions
|
|
|