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 26namespace content { 27namespace { 28const char kUploadContentType[] = "multipart/form-data"; 29const char kMultipartBoundary[] = 30 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----"; 31 32const int kHttpResponseOk = 200; 33 34} // namespace 35 36TraceUploader::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 49TraceUploader::~TraceUploader() { 50 DCHECK_CURRENTLY_ON(BrowserThread::UI); 51} 52 53void 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 74void 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 85void 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 119void 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 127void 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 147void 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 162bool 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 196void 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