Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(96)

Side by Side Diff: components/webrtc_log_uploader/webrtc_log_uploader.cc

Issue 14329020: Implementing uploading of a WebRTC diagnostic log. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Code review. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 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/webrtc_log_uploader/webrtc_log_uploader.h"
6
7 #if defined(USE_SYSTEM_LIBBZ2)
8 #include <bzlib.h>
9 #else
10 #include "third_party/bzip2/bzlib.h"
11 #endif
12
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "base/partial_circular_buffer.h"
16 #include "base/shared_memory.h"
17 #include "base/stringprintf.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "net/base/network_delegate.h"
20 #include "net/proxy/proxy_config.h"
21 #include "net/proxy/proxy_config_service.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "net/url_request/url_request_context.h"
24 #include "net/url_request/url_request_context_builder.h"
25 #include "net/url_request/url_request_context_getter.h"
26
27 namespace components {
28
29 namespace {
30
31 const int kLogCountLimit = 5;
32 const uint32 kIntermediateCompressionBufferSize = 256 * 1024; // 256 KB
33
34 const char kUploadURL[] = "https://clients2.google.com/cr/report";
35 const char kUploadContentType[] = "multipart/form-data";
36 const char kMultipartBoundary[] =
37 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
38
39 // Config getter that always returns direct settings.
40 class ProxyConfigServiceDirect : public net::ProxyConfigService {
41 public:
42 // ProxyConfigService implementation.
43 virtual void AddObserver(Observer* observer) OVERRIDE {}
44 virtual void RemoveObserver(Observer* observer) OVERRIDE {}
45 virtual ConfigAvailability GetLatestProxyConfig(
46 net::ProxyConfig* config) OVERRIDE {
47 *config = net::ProxyConfig::CreateDirect();
48 return CONFIG_VALID;
49 }
50 };
51
52 } // namespace
53
54 class WebRtcLogURLRequestContextGetter : public net::URLRequestContextGetter {
55 public:
56 WebRtcLogURLRequestContextGetter() {}
57
58 // net::URLRequestContextGetter implementation.
59 virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE {
60 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
61
62 if (!url_request_context_) {
63 net::URLRequestContextBuilder builder;
64 #if defined(OS_LINUX) || defined(OS_ANDROID)
65 builder.set_proxy_config_service(new ProxyConfigServiceDirect());
66 #endif
67 url_request_context_.reset(builder.Build());
68 }
69 CHECK(url_request_context_.get());
vabr (Chromium) 2013/05/13 08:55:50 nit: Put this CHECK into the if branch above?
Henrik Grunell 2013/05/14 13:48:50 Done.
70
71 return url_request_context_.get();
72 }
73
74 virtual scoped_refptr<base::SingleThreadTaskRunner>
75 GetNetworkTaskRunner() const OVERRIDE {
76 return content::BrowserThread::GetMessageLoopProxyForThread(
77 content::BrowserThread::IO);
78 }
79
80 private:
81 virtual ~WebRtcLogURLRequestContextGetter() {}
82
83 // NULL if not yet initialized. Otherwise, it is the URLRequestContext
84 // instance that was lazily created by GetURLRequestContext().
85 // Access only from the IO thread.
86 scoped_ptr<net::URLRequestContext> url_request_context_;
87
88 DISALLOW_COPY_AND_ASSIGN(WebRtcLogURLRequestContextGetter);
89 };
90
91
92 WebRtcLogUploader::WebRtcLogUploader()
93 : log_count_(0),
94 multipart_file_(base::kInvalidPlatformFileValue) {
95 }
96
97 WebRtcLogUploader::~WebRtcLogUploader() {
98 }
99
100 void WebRtcLogUploader::OnURLFetchComplete(
101 const net::URLFetcher* source) {
102 std::vector<net::URLFetcher*>::iterator it;
103 for (it = url_fetchers_.begin(); it != url_fetchers_.end(); ++it) {
104 if (*it == source) {
105 url_fetchers_.erase(it);
106 break;
107 }
108 }
109 delete source;
110 }
111
112 void WebRtcLogUploader::OnURLFetchUploadProgress(
113 const net::URLFetcher* source, int64 current, int64 total) {
114 }
115
116 bool WebRtcLogUploader::ApplyForStartLogging() {
117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
118 if (log_count_ < kLogCountLimit) {
119 ++log_count_;
120 return true;
121 }
122 return false;
123 }
124
125 void WebRtcLogUploader::UploadLog(scoped_ptr<base::SharedMemory> shared_memory,
126 uint32 length) {
127 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
128 DCHECK(shared_memory);
129 DCHECK(shared_memory->memory());
130
131 base::FilePath upload_file_path;
132 if (file_util::CreateTemporaryFile(&upload_file_path)) {
133 SetupMultipartFile(reinterpret_cast<uint8*>(shared_memory->memory()),
134 length, upload_file_path);
135
136 if (!request_context_getter_)
137 request_context_getter_ = new WebRtcLogURLRequestContextGetter();
138
139 std::string content_type = kUploadContentType;
140 content_type.append("; boundary=");
141 content_type.append(kMultipartBoundary);
vabr (Chromium) 2013/05/13 08:55:50 |content_type| looks like a composition of compile
Henrik Grunell 2013/05/14 13:48:50 Yes it could (and I agree it's nicer), but the bou
vabr (Chromium) 2013/05/14 15:24:41 I see three possibilities: 1. #define kMultipartB
vabr (Chromium) 2013/05/14 15:24:41 On 2013/05/14 13:48:50, Henrik Grunell wrote: > On
Henrik Grunell 2013/05/15 20:17:53 Yes I did think of these options, I prefer 3 (simp
142
143 net::URLFetcher* url_fetcher =
144 net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this);
145 url_fetcher->SetRequestContext(request_context_getter_);
146 url_fetcher->SetUploadFilePath(
147 content_type, upload_file_path, 0, kuint64max,
148 content::BrowserThread::GetMessageLoopProxyForThread(
149 content::BrowserThread::FILE));
150 url_fetcher->Start();
151 url_fetchers_.push_back(url_fetcher);
152 } else {
153 DLOG(ERROR) << "Could not create temporary file";
154 }
155
156 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
157 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
158 }
159
160 void WebRtcLogUploader::SetupMultipartFile(
161 uint8* log_buffer, uint32 log_buffer_length,
162 const base::FilePath& upload_file_path) {
163 int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
164 base::PLATFORM_FILE_WRITE;
165 bool created = false;
166 base::PlatformFileError error = base::PLATFORM_FILE_OK;
167 multipart_file_ =
168 CreatePlatformFile(upload_file_path, flags, &created, &error);
169 DCHECK(created);
170 DCHECK(error == base::PLATFORM_FILE_OK);
171
172 // TODO(grunell): Correct product name.
173 AddPairString("prod", "Chrome");
174 // TODO(grunell): Correct version.
175 AddPairString("ver", "0.0.1.1-dev-test");
176 AddPairString("guid", "0");
177 AddPairString("type", "log");
178
179 AddUrlChunks();
180
181 AddLogData(log_buffer, log_buffer_length);
182
183 std::stringstream ss;
184 ss << "--" << kMultipartBoundary << "--" << "\r\n";
185 base::WritePlatformFileAtCurrentPos(
186 multipart_file_, ss.str().data(), ss.str().size());
187
188 DCHECK(base::ClosePlatformFile(multipart_file_));
189 }
190
191 void WebRtcLogUploader::AddPairString(const std::string& key,
192 const std::string& value) {
vabr (Chromium) 2013/05/13 08:55:50 nit: correct indenting
Henrik Grunell 2013/05/14 13:48:50 Done.
193 std::string str;
194 AddMultipartValueForUpload(key, value, kMultipartBoundary, "", &str);
195 base::WritePlatformFileAtCurrentPos(multipart_file_, str.c_str(), str.size());
196 }
197
198 void WebRtcLogUploader::AddUrlChunks() {
199 // TODO(grunell): Implement.
200 }
201
202 void WebRtcLogUploader::AddLogData(uint8* log_buffer,
203 uint32 log_buffer_length) {
204 std::stringstream ss;
vabr (Chromium) 2013/05/13 08:55:50 I don't think the Style Guide allows streams http:
Henrik Grunell 2013/05/14 13:48:50 OK, sure. Done.
205 ss << "--" << kMultipartBoundary << "\r\n";
206 ss << "Content-Disposition: form-data; name=\"log\"";
207 ss << "; filename=\"log.bz2\"" << "\r\n";
208 ss << "Content-Type: application/x-bzip" << "\r\n";
209 ss << "\r\n";
210 base::WritePlatformFileAtCurrentPos(
211 multipart_file_, ss.str().data(), ss.str().size());
212
213 CompressLog(log_buffer, log_buffer_length, multipart_file_);
214
215 std::string end_str = "\r\n";
216 base::WritePlatformFileAtCurrentPos(
217 multipart_file_, end_str.c_str(), end_str.size());
218 }
219
220 // TODO(grunell): This is copied from cloud_print_helpers.cc. Break out to its
221 // own file.
222 void WebRtcLogUploader::AddMultipartValueForUpload(
223 const std::string& value_name, const std::string& value,
224 const std::string& mime_boundary, const std::string& content_type,
225 std::string* post_data) {
226 DCHECK(post_data);
227 // First line is the boundary
228 post_data->append("--" + mime_boundary + "\r\n");
229 // Next line is the Content-disposition
230 post_data->append(base::StringPrintf("Content-Disposition: form-data; "
231 "name=\"%s\"\r\n", value_name.c_str()));
232 if (!content_type.empty()) {
233 // If Content-type is specified, the next line is that
234 post_data->append(base::StringPrintf("Content-Type: %s\r\n",
235 content_type.c_str()));
236 }
237 // Leave an empty line and append the value.
238 post_data->append(base::StringPrintf("\r\n%s\r\n", value.c_str()));
239 }
240
241 void WebRtcLogUploader::CompressLog(uint8* input,
242 uint32 input_size,
243 base::PlatformFile output_file) {
244 base::PartialCircularBuffer read_pcb(input, input_size);
245
246 bz_stream stream = {0};
247 int result = BZ2_bzCompressInit(&stream, 3, 0, 0);
248 DCHECK(result == BZ_OK);
249
250 scoped_ptr<uint8[]> intermediate_buffer(
251 new uint8[kIntermediateCompressionBufferSize]);
252 scoped_ptr<uint8[]> compressed(
253 new uint8[kIntermediateCompressionBufferSize]);
254 memset(intermediate_buffer.get(), 0, kIntermediateCompressionBufferSize);
255 memset(compressed.get(), 0, kIntermediateCompressionBufferSize);
256 uint32 read = read_pcb.Read(intermediate_buffer.get(),
257 kIntermediateCompressionBufferSize);
258
259 stream.next_in = reinterpret_cast<char*>(&intermediate_buffer.get()[0]);
260 stream.avail_in = read;
261 char* compressed_char_ptr = reinterpret_cast<char*>(&compressed.get()[0]);
262
263 // Keeps track of written output.
264 unsigned int last_total_out = stream.total_out_lo32;
265
266 while (read == kIntermediateCompressionBufferSize) {
267 stream.next_out = compressed_char_ptr;
268 stream.avail_out = kIntermediateCompressionBufferSize;
269 result = BZ2_bzCompress(&stream, BZ_RUN);
270 DCHECK(result == BZ_RUN_OK);
271 base::WritePlatformFileAtCurrentPos(output_file, compressed_char_ptr,
272 stream.total_out_lo32 - last_total_out);
273 last_total_out = stream.total_out_lo32;
274 read = read_pcb.Read(intermediate_buffer.get(),
275 kIntermediateCompressionBufferSize);
276 stream.next_in = reinterpret_cast<char*>(&intermediate_buffer.get()[0]);
277 stream.avail_in = read;
278 }
279
280 do {
281 stream.next_out = compressed_char_ptr;
282 stream.avail_out = kIntermediateCompressionBufferSize;
283 result = BZ2_bzCompress(&stream, BZ_FINISH);
284 base::WritePlatformFileAtCurrentPos(output_file, compressed_char_ptr,
285 stream.total_out_lo32 - last_total_out);
286 last_total_out = stream.total_out_lo32;
287 } while (result == BZ_FINISH_OK);
288 DCHECK(result == BZ_STREAM_END);
289
290 result = BZ2_bzCompressEnd(&stream);
291 DCHECK(result == BZ_OK);
292 }
293
294 void WebRtcLogUploader::DecreaseLogCount() {
295 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
296 --log_count_;
297 }
298
299 } // namespace components
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698