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 "components/domain_reliability/uploader.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/memory/scoped_vector.h"
10#include "base/metrics/sparse_histogram.h"
11#include "base/stl_util.h"
12#include "base/supports_user_data.h"
13#include "net/base/load_flags.h"
14#include "net/url_request/url_fetcher.h"
15#include "net/url_request/url_fetcher_delegate.h"
16#include "net/url_request/url_request_context_getter.h"
17
18namespace domain_reliability {
19
20namespace {
21
22const char* kJsonMimeType = "application/json; charset=utf-8";
23
24class UploadUserData : public base::SupportsUserData::Data {
25 public:
26  static net::URLFetcher::CreateDataCallback CreateCreateDataCallback() {
27    return base::Bind(&UploadUserData::CreateUploadUserData);
28  }
29
30  static const void* kUserDataKey;
31
32 private:
33  static base::SupportsUserData::Data* CreateUploadUserData() {
34    return new UploadUserData();
35  }
36};
37
38const void* UploadUserData::kUserDataKey =
39    static_cast<const void*>(&UploadUserData::kUserDataKey);
40
41class DomainReliabilityUploaderImpl
42    : public DomainReliabilityUploader, net::URLFetcherDelegate {
43 public:
44  DomainReliabilityUploaderImpl(const scoped_refptr<
45      net::URLRequestContextGetter>& url_request_context_getter)
46      : url_request_context_getter_(url_request_context_getter),
47        discard_uploads_(true) {}
48
49  virtual ~DomainReliabilityUploaderImpl() {
50    // Delete any in-flight URLFetchers.
51    STLDeleteContainerPairFirstPointers(
52        upload_callbacks_.begin(), upload_callbacks_.end());
53  }
54
55  // DomainReliabilityUploader implementation:
56  virtual void UploadReport(
57      const std::string& report_json,
58      const GURL& upload_url,
59      const DomainReliabilityUploader::UploadCallback& callback) OVERRIDE {
60    VLOG(1) << "Uploading report to " << upload_url;
61    VLOG(2) << "Report JSON: " << report_json;
62
63    if (discard_uploads_) {
64      VLOG(1) << "Discarding report instead of uploading.";
65      callback.Run(true);
66      return;
67    }
68
69    net::URLFetcher* fetcher =
70        net::URLFetcher::Create(0, upload_url, net::URLFetcher::POST, this);
71    fetcher->SetRequestContext(url_request_context_getter_.get());
72    fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
73                          net::LOAD_DO_NOT_SAVE_COOKIES);
74    fetcher->SetUploadData(kJsonMimeType, report_json);
75    fetcher->SetAutomaticallyRetryOn5xx(false);
76    fetcher->SetURLRequestUserData(
77        UploadUserData::kUserDataKey,
78        UploadUserData::CreateCreateDataCallback());
79    fetcher->Start();
80
81    upload_callbacks_[fetcher] = callback;
82  }
83
84  virtual void set_discard_uploads(bool discard_uploads) OVERRIDE {
85    discard_uploads_ = discard_uploads;
86    VLOG(1) << "Setting discard_uploads to " << discard_uploads;
87  }
88
89  // net::URLFetcherDelegate implementation:
90  virtual void OnURLFetchComplete(
91      const net::URLFetcher* fetcher) OVERRIDE {
92    DCHECK(fetcher);
93
94    UploadCallbackMap::iterator callback_it = upload_callbacks_.find(fetcher);
95    DCHECK(callback_it != upload_callbacks_.end());
96
97    VLOG(1) << "Upload finished with " << fetcher->GetResponseCode();
98
99    UMA_HISTOGRAM_SPARSE_SLOWLY("DomainReliability.UploadResponseCode",
100                                fetcher->GetResponseCode());
101
102    bool success = fetcher->GetResponseCode() == 200;
103    callback_it->second.Run(success);
104
105    delete callback_it->first;
106    upload_callbacks_.erase(callback_it);
107  }
108
109 private:
110  using DomainReliabilityUploader::UploadCallback;
111  typedef std::map<const net::URLFetcher*, UploadCallback> UploadCallbackMap;
112
113  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
114  UploadCallbackMap upload_callbacks_;
115  bool discard_uploads_;
116};
117
118}  // namespace
119
120DomainReliabilityUploader::DomainReliabilityUploader() {}
121DomainReliabilityUploader::~DomainReliabilityUploader() {}
122
123// static
124scoped_ptr<DomainReliabilityUploader> DomainReliabilityUploader::Create(
125    const scoped_refptr<net::URLRequestContextGetter>&
126        url_request_context_getter) {
127  return scoped_ptr<DomainReliabilityUploader>(
128      new DomainReliabilityUploaderImpl(url_request_context_getter));
129}
130
131// static
132bool DomainReliabilityUploader::URLRequestIsUpload(
133    const net::URLRequest& request) {
134  return request.GetUserData(UploadUserData::kUserDataKey) != NULL;
135}
136
137}  // namespace domain_reliability
138