File: device_description_fetcher.cc

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (150 lines) | stat: -rw-r--r-- 5,295 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright (c) 2017 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 "content/public/browser/browser_thread.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"

using content::BrowserThread;

constexpr char kApplicationUrlHeaderName[] = "Application-URL";
constexpr int kMaxRetries = 3;
// DIAL devices are unlikely to expose uPnP functions other than DIAL, so 256kb
// should be more than sufficient.
constexpr int kMaxDescriptionSizeBytes = 262144;

namespace extensions {
namespace api {
namespace dial {

DeviceDescriptionFetcher::DeviceDescriptionFetcher(
    const GURL& device_description_url,
    Profile* profile,
    base::OnceCallback<void(const DialDeviceDescriptionData&)> 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_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(profile_);
  DCHECK(device_description_url_.is_valid());
}

DeviceDescriptionFetcher::~DeviceDescriptionFetcher() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
}

void DeviceDescriptionFetcher::Start() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(!fetcher_);

  // DIAL returns device descriptions via GET request.
  fetcher_ =
      net::URLFetcher::Create(kURLFetcherIDForTest, device_description_url_,
                              net::URLFetcher::GET, this);

  // net::LOAD_BYPASS_PROXY: Proxies almost certainly hurt more cases than they
  //     help.
  // net::LOAD_DISABLE_CACHE: The request should not touch the cache.
  // net::LOAD_DO_NOT_{SAVE,SEND}_COOKIES: The request should not touch cookies.
  // net::LOAD_DO_NOT_SEND_AUTH_DATA: The request should not send auth data.
  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);

  // Section 5.4 of the DIAL spec prohibits 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) {
  DCHECK_EQ(fetcher_.get(), 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 specifically not checking this header.
  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(DialDeviceDescriptionData(std::move(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 dial
}  // namespace api
}  // namespace extensions