• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "content/browser/tracing/trace_uploader.h"
6 
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/shared_memory.h"
10 #include "base/path_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/base/mime_util.h"
16 #include "net/base/network_delegate.h"
17 #include "net/proxy/proxy_config.h"
18 #include "net/proxy/proxy_config_service.h"
19 #include "net/url_request/url_fetcher.h"
20 #include "net/url_request/url_request_context.h"
21 #include "net/url_request/url_request_context_builder.h"
22 #include "net/url_request/url_request_context_getter.h"
23 #include "third_party/zlib/zlib.h"
24 #include "url/gurl.h"
25 
26 namespace content {
27 namespace {
28 const char kUploadContentType[] = "multipart/form-data";
29 const char kMultipartBoundary[] =
30     "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
31 
32 const int kHttpResponseOk = 200;
33 
34 }  // namespace
35 
TraceUploader(const std::string & product,const std::string & version,const std::string & upload_url,net::URLRequestContextGetter * request_context)36 TraceUploader::TraceUploader(const std::string& product,
37                              const std::string& version,
38                              const std::string& upload_url,
39                              net::URLRequestContextGetter* request_context)
40     : product_(product),
41       version_(version),
42       upload_url_(upload_url),
43       request_context_(request_context) {
44   DCHECK(!product_.empty());
45   DCHECK(!version_.empty());
46   DCHECK(!upload_url_.empty());
47 }
48 
~TraceUploader()49 TraceUploader::~TraceUploader() {
50   DCHECK_CURRENTLY_ON(BrowserThread::UI);
51 }
52 
OnURLFetchComplete(const net::URLFetcher * source)53 void TraceUploader::OnURLFetchComplete(const net::URLFetcher* source) {
54   DCHECK_CURRENTLY_ON(BrowserThread::UI);
55   DCHECK_EQ(source, url_fetcher_.get());
56   int response_code = source->GetResponseCode();
57   string report_id;
58   string error_message;
59   bool success = (response_code == kHttpResponseOk);
60   if (success) {
61     source->GetResponseAsString(&report_id);
62   } else {
63     error_message = "Uploading failed, response code: " +
64                     base::IntToString(response_code);
65   }
66 
67   BrowserThread::PostTask(
68       content::BrowserThread::UI,
69       FROM_HERE,
70       base::Bind(done_callback_, success, report_id, error_message));
71   url_fetcher_.reset();
72 }
73 
OnURLFetchUploadProgress(const net::URLFetcher * source,int64 current,int64 total)74 void TraceUploader::OnURLFetchUploadProgress(
75     const net::URLFetcher* source, int64 current, int64 total) {
76   DCHECK(url_fetcher_.get());
77 
78   LOG(WARNING) << "Upload progress: " << current << " of " << total;
79   BrowserThread::PostTask(
80       content::BrowserThread::UI,
81       FROM_HERE,
82       base::Bind(progress_callback_, current, total));
83 }
84 
DoUpload(const std::string & file_contents,UploadProgressCallback progress_callback,UploadDoneCallback done_callback)85 void TraceUploader::DoUpload(
86     const std::string& file_contents,
87     UploadProgressCallback progress_callback,
88     UploadDoneCallback done_callback) {
89   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
90   DCHECK(!url_fetcher_.get());
91 
92   progress_callback_ = progress_callback;
93   done_callback_ = done_callback;
94 
95   if (url_fetcher_.get()) {
96     OnUploadError("Already uploading.");
97   }
98 
99   scoped_ptr<char[]> compressed_contents(new char[kMaxUploadBytes]);
100   int compressed_bytes;
101   if (!Compress(file_contents, kMaxUploadBytes, compressed_contents.get(),
102                 &compressed_bytes)) {
103     OnUploadError("Compressing file failed.");
104     return;
105   }
106 
107   std::string post_data;
108   SetupMultipart("trace.json.gz",
109                  std::string(compressed_contents.get(), compressed_bytes),
110                  &post_data);
111 
112   content::BrowserThread::PostTask(
113       content::BrowserThread::UI, FROM_HERE,
114       base::Bind(&TraceUploader::CreateAndStartURLFetcher,
115                  base::Unretained(this),
116                  post_data));
117 }
118 
OnUploadError(std::string error_message)119 void TraceUploader::OnUploadError(std::string error_message) {
120   LOG(ERROR) << error_message;
121   content::BrowserThread::PostTask(
122       content::BrowserThread::UI,
123       FROM_HERE,
124       base::Bind(done_callback_, false, "", error_message));
125 }
126 
SetupMultipart(const std::string & trace_filename,const std::string & trace_contents,std::string * post_data)127 void TraceUploader::SetupMultipart(const std::string& trace_filename,
128                                    const std::string& trace_contents,
129                                    std::string* post_data) {
130   net::AddMultipartValueForUpload("prod", product_, kMultipartBoundary, "",
131                                   post_data);
132   net::AddMultipartValueForUpload("ver", version_ + "-trace",
133                                   kMultipartBoundary, "", post_data);
134   net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
135                                   "", post_data);
136   net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary,
137                                   "", post_data);
138   // No minidump means no need for crash to process the report.
139   net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary,
140                                   "", post_data);
141 
142   AddTraceFile(trace_filename, trace_contents, post_data);
143 
144   net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
145 }
146 
AddTraceFile(const std::string & trace_filename,const std::string & trace_contents,std::string * post_data)147 void TraceUploader::AddTraceFile(const std::string& trace_filename,
148                                  const std::string& trace_contents,
149                                  std::string* post_data) {
150   post_data->append("--");
151   post_data->append(kMultipartBoundary);
152   post_data->append("\r\n");
153   post_data->append("Content-Disposition: form-data; name=\"trace\"");
154   post_data->append("; filename=\"");
155   post_data->append(trace_filename);
156   post_data->append("\"\r\n");
157   post_data->append("Content-Type: application/gzip\r\n\r\n");
158   post_data->append(trace_contents);
159   post_data->append("\r\n");
160 }
161 
Compress(std::string input,int max_compressed_bytes,char * compressed,int * compressed_bytes)162 bool TraceUploader::Compress(std::string input,
163                              int max_compressed_bytes,
164                              char* compressed,
165                              int* compressed_bytes) {
166   DCHECK(compressed);
167   DCHECK(compressed_bytes);
168   z_stream stream = {0};
169   int result = deflateInit2(&stream,
170                             Z_DEFAULT_COMPRESSION,
171                             Z_DEFLATED,
172                             // 16 is added to produce a gzip header + trailer.
173                             MAX_WBITS + 16,
174                             8,  // memLevel = 8 is default.
175                             Z_DEFAULT_STRATEGY);
176   DCHECK_EQ(Z_OK, result);
177   stream.next_in = reinterpret_cast<uint8*>(&input[0]);
178   stream.avail_in = input.size();
179   stream.next_out = reinterpret_cast<uint8*>(compressed);
180   stream.avail_out = max_compressed_bytes;
181   // Do a one-shot compression. This will return Z_STREAM_END only if |output|
182   // is large enough to hold all compressed data.
183   result = deflate(&stream, Z_FINISH);
184   bool success = (result == Z_STREAM_END);
185   result = deflateEnd(&stream);
186   DCHECK(result == Z_OK || result == Z_DATA_ERROR);
187 
188   if (success)
189     *compressed_bytes = max_compressed_bytes - stream.avail_out;
190 
191   LOG(WARNING) << "input size: " << input.size()
192                << ", output size: " << *compressed_bytes;
193   return success;
194 }
195 
CreateAndStartURLFetcher(const std::string & post_data)196 void TraceUploader::CreateAndStartURLFetcher(const std::string& post_data) {
197   DCHECK_CURRENTLY_ON(BrowserThread::UI);
198   DCHECK(!url_fetcher_.get());
199 
200   std::string content_type = kUploadContentType;
201   content_type.append("; boundary=");
202   content_type.append(kMultipartBoundary);
203 
204   url_fetcher_.reset(
205       net::URLFetcher::Create(GURL(upload_url_), net::URLFetcher::POST, this));
206   url_fetcher_->SetRequestContext(request_context_);
207   url_fetcher_->SetUploadData(content_type, post_data);
208   url_fetcher_->Start();
209 }
210 
211 }  // namespace content
212