Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / extensions / browser / api / cast_channel / logger.cc
blob9390210bdce6ab66961d4d0ea27d51731ca13cf9
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.
5 #include "extensions/browser/api/cast_channel/logger.h"
7 #include <string>
9 #include "base/strings/string_util.h"
10 #include "base/time/tick_clock.h"
11 #include "extensions/browser/api/cast_channel/cast_auth_util.h"
12 #include "extensions/browser/api/cast_channel/cast_socket.h"
13 #include "extensions/browser/api/cast_channel/logger_util.h"
14 #include "net/base/net_errors.h"
15 #include "third_party/zlib/zlib.h"
17 namespace extensions {
18 namespace core_api {
19 namespace cast_channel {
21 using net::IPEndPoint;
22 using proto::AggregatedSocketEvent;
23 using proto::EventType;
24 using proto::Log;
25 using proto::SocketEvent;
27 namespace {
29 const char* kInternalNamespacePrefix = "com.google.cast";
31 proto::ChallengeReplyErrorType ChallegeReplyErrorToProto(
32 AuthResult::ErrorType error_type) {
33 switch (error_type) {
34 case AuthResult::ERROR_NONE:
35 return proto::CHALLENGE_REPLY_ERROR_NONE;
36 case AuthResult::ERROR_PEER_CERT_EMPTY:
37 return proto::CHALLENGE_REPLY_ERROR_PEER_CERT_EMPTY;
38 case AuthResult::ERROR_WRONG_PAYLOAD_TYPE:
39 return proto::CHALLENGE_REPLY_ERROR_WRONG_PAYLOAD_TYPE;
40 case AuthResult::ERROR_NO_PAYLOAD:
41 return proto::CHALLENGE_REPLY_ERROR_NO_PAYLOAD;
42 case AuthResult::ERROR_PAYLOAD_PARSING_FAILED:
43 return proto::CHALLENGE_REPLY_ERROR_PAYLOAD_PARSING_FAILED;
44 case AuthResult::ERROR_MESSAGE_ERROR:
45 return proto::CHALLENGE_REPLY_ERROR_MESSAGE_ERROR;
46 case AuthResult::ERROR_NO_RESPONSE:
47 return proto::CHALLENGE_REPLY_ERROR_NO_RESPONSE;
48 case AuthResult::ERROR_FINGERPRINT_NOT_FOUND:
49 return proto::CHALLENGE_REPLY_ERROR_FINGERPRINT_NOT_FOUND;
50 case AuthResult::ERROR_CERT_PARSING_FAILED:
51 return proto::CHALLENGE_REPLY_ERROR_CERT_PARSING_FAILED;
52 case AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA:
53 return proto::CHALLENGE_REPLY_ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA;
54 case AuthResult::ERROR_CANNOT_EXTRACT_PUBLIC_KEY:
55 return proto::CHALLENGE_REPLY_ERROR_CANNOT_EXTRACT_PUBLIC_KEY;
56 case AuthResult::ERROR_SIGNED_BLOBS_MISMATCH:
57 return proto::CHALLENGE_REPLY_ERROR_SIGNED_BLOBS_MISMATCH;
58 default:
59 NOTREACHED();
60 return proto::CHALLENGE_REPLY_ERROR_NONE;
64 scoped_ptr<char[]> Compress(const std::string& input, size_t* length) {
65 *length = 0;
66 z_stream stream = {0};
67 int result = deflateInit2(&stream,
68 Z_DEFAULT_COMPRESSION,
69 Z_DEFLATED,
70 // 16 is added to produce a gzip header + trailer.
71 MAX_WBITS + 16,
72 8, // memLevel = 8 is default.
73 Z_DEFAULT_STRATEGY);
74 DCHECK_EQ(Z_OK, result);
76 size_t out_size = deflateBound(&stream, input.size());
77 scoped_ptr<char[]> out(new char[out_size]);
79 static_assert(sizeof(uint8) == sizeof(char),
80 "uint8 char should be of different sizes");
82 stream.next_in = reinterpret_cast<uint8*>(const_cast<char*>(input.data()));
83 stream.avail_in = input.size();
84 stream.next_out = reinterpret_cast<uint8*>(out.get());
85 stream.avail_out = out_size;
87 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
88 // is large enough to hold all compressed data.
89 result = deflate(&stream, Z_FINISH);
91 bool success = (result == Z_STREAM_END);
93 if (!success)
94 VLOG(2) << "deflate() failed. Result: " << result;
96 result = deflateEnd(&stream);
97 DCHECK(result == Z_OK || result == Z_DATA_ERROR);
99 if (success)
100 *length = out_size - stream.avail_out;
102 return out.Pass();
105 // Propagate any error fields set in |event| to |last_errors|. If any error
106 // field in |event| is set, then also set |last_errors->event_type|.
107 void MaybeSetLastErrors(const SocketEvent& event, LastErrors* last_errors) {
108 if (event.has_net_return_value() &&
109 event.net_return_value() < net::ERR_IO_PENDING) {
110 last_errors->net_return_value = event.net_return_value();
111 last_errors->event_type = event.type();
113 if (event.has_challenge_reply_error_type()) {
114 last_errors->challenge_reply_error_type =
115 event.challenge_reply_error_type();
116 last_errors->event_type = event.type();
118 if (event.has_nss_error_code()) {
119 last_errors->nss_error_code = event.nss_error_code();
120 last_errors->event_type = event.type();
124 } // namespace
126 Logger::AggregatedSocketEventLog::AggregatedSocketEventLog() {
129 Logger::AggregatedSocketEventLog::~AggregatedSocketEventLog() {
132 Logger::Logger(scoped_ptr<base::TickClock> clock,
133 base::TimeTicks unix_epoch_time_ticks)
134 : clock_(clock.Pass()), unix_epoch_time_ticks_(unix_epoch_time_ticks) {
135 DCHECK(clock_);
137 // Logger may not be necessarily be created on the IO thread, but logging
138 // happens exclusively there.
139 thread_checker_.DetachFromThread();
142 Logger::~Logger() {
145 void Logger::LogNewSocketEvent(const CastSocket& cast_socket) {
146 DCHECK(thread_checker_.CalledOnValidThread());
148 SocketEvent event = CreateEvent(proto::CAST_SOCKET_CREATED);
149 AggregatedSocketEvent& aggregated_socket_event =
150 LogSocketEvent(cast_socket.id(), event);
152 const net::IPAddressNumber& ip = cast_socket.ip_endpoint().address();
153 aggregated_socket_event.set_endpoint_id(ip.back());
154 aggregated_socket_event.set_channel_auth_type(cast_socket.channel_auth() ==
155 CHANNEL_AUTH_TYPE_SSL
156 ? proto::SSL
157 : proto::SSL_VERIFIED);
160 void Logger::LogSocketEvent(int channel_id, EventType event_type) {
161 DCHECK(thread_checker_.CalledOnValidThread());
163 LogSocketEventWithDetails(channel_id, event_type, std::string());
166 void Logger::LogSocketEventWithDetails(int channel_id,
167 EventType event_type,
168 const std::string& details) {
169 DCHECK(thread_checker_.CalledOnValidThread());
171 SocketEvent event = CreateEvent(event_type);
172 if (!details.empty())
173 event.set_details(details);
175 LogSocketEvent(channel_id, event);
178 void Logger::LogSocketEventWithRv(int channel_id,
179 EventType event_type,
180 int rv) {
181 DCHECK(thread_checker_.CalledOnValidThread());
183 SocketEvent event = CreateEvent(event_type);
184 event.set_net_return_value(rv);
186 AggregatedSocketEvent& aggregated_socket_event =
187 LogSocketEvent(channel_id, event);
189 if ((event_type == proto::SOCKET_READ || event_type == proto::SOCKET_WRITE) &&
190 rv > 0) {
191 if (event_type == proto::SOCKET_READ) {
192 aggregated_socket_event.set_bytes_read(
193 aggregated_socket_event.bytes_read() + rv);
194 } else {
195 aggregated_socket_event.set_bytes_written(
196 aggregated_socket_event.bytes_written() + rv);
201 void Logger::LogSocketReadyState(int channel_id, proto::ReadyState new_state) {
202 DCHECK(thread_checker_.CalledOnValidThread());
204 SocketEvent event = CreateEvent(proto::READY_STATE_CHANGED);
205 event.set_ready_state(new_state);
207 LogSocketEvent(channel_id, event);
210 void Logger::LogSocketConnectState(int channel_id,
211 proto::ConnectionState new_state) {
212 DCHECK(thread_checker_.CalledOnValidThread());
214 SocketEvent event = CreateEvent(proto::CONNECTION_STATE_CHANGED);
215 event.set_connection_state(new_state);
217 LogSocketEvent(channel_id, event);
220 void Logger::LogSocketReadState(int channel_id, proto::ReadState new_state) {
221 DCHECK(thread_checker_.CalledOnValidThread());
223 SocketEvent event = CreateEvent(proto::READ_STATE_CHANGED);
224 event.set_read_state(new_state);
226 LogSocketEvent(channel_id, event);
229 void Logger::LogSocketWriteState(int channel_id, proto::WriteState new_state) {
230 DCHECK(thread_checker_.CalledOnValidThread());
232 SocketEvent event = CreateEvent(proto::WRITE_STATE_CHANGED);
233 event.set_write_state(new_state);
235 LogSocketEvent(channel_id, event);
238 void Logger::LogSocketErrorState(int channel_id, proto::ErrorState new_state) {
239 DCHECK(thread_checker_.CalledOnValidThread());
241 SocketEvent event = CreateEvent(proto::ERROR_STATE_CHANGED);
242 event.set_error_state(new_state);
244 LogSocketEvent(channel_id, event);
247 void Logger::LogSocketEventForMessage(int channel_id,
248 EventType event_type,
249 const std::string& message_namespace,
250 const std::string& details) {
251 DCHECK(thread_checker_.CalledOnValidThread());
253 SocketEvent event = CreateEvent(event_type);
254 if (StartsWithASCII(message_namespace, kInternalNamespacePrefix, false))
255 event.set_message_namespace(message_namespace);
256 event.set_details(details);
258 LogSocketEvent(channel_id, event);
261 void Logger::LogSocketChallengeReplyEvent(int channel_id,
262 const AuthResult& auth_result) {
263 DCHECK(thread_checker_.CalledOnValidThread());
265 SocketEvent event = CreateEvent(proto::AUTH_CHALLENGE_REPLY);
266 event.set_challenge_reply_error_type(
267 ChallegeReplyErrorToProto(auth_result.error_type));
268 if (auth_result.nss_error_code != 0)
269 event.set_nss_error_code(auth_result.nss_error_code);
271 LogSocketEvent(channel_id, event);
274 SocketEvent Logger::CreateEvent(EventType event_type) {
275 SocketEvent event;
276 event.set_type(event_type);
277 event.set_timestamp_micros(clock_->NowTicks().ToInternalValue() -
278 unix_epoch_time_ticks_.ToInternalValue());
279 return event;
282 AggregatedSocketEvent& Logger::LogSocketEvent(int channel_id,
283 const SocketEvent& socket_event) {
284 AggregatedSocketEventLogMap::iterator it =
285 aggregated_socket_events_.find(channel_id);
286 if (it == aggregated_socket_events_.end()) {
287 if (aggregated_socket_events_.size() >= kMaxSocketsToLog) {
288 AggregatedSocketEventLogMap::iterator erase_it =
289 aggregated_socket_events_.begin();
291 log_.set_num_evicted_aggregated_socket_events(
292 log_.num_evicted_aggregated_socket_events() + 1);
293 log_.set_num_evicted_socket_events(
294 log_.num_evicted_socket_events() +
295 erase_it->second->socket_events.size());
297 aggregated_socket_events_.erase(erase_it);
300 it = aggregated_socket_events_
301 .insert(std::make_pair(
302 channel_id, make_linked_ptr(new AggregatedSocketEventLog)))
303 .first;
304 it->second->aggregated_socket_event.set_id(channel_id);
307 std::deque<proto::SocketEvent>& socket_events = it->second->socket_events;
308 if (socket_events.size() >= kMaxEventsPerSocket) {
309 socket_events.pop_front();
310 log_.set_num_evicted_socket_events(log_.num_evicted_socket_events() + 1);
312 socket_events.push_back(socket_event);
314 MaybeSetLastErrors(socket_event, &(it->second->last_errors));
316 return it->second->aggregated_socket_event;
319 scoped_ptr<char[]> Logger::GetLogs(size_t* length) const {
320 *length = 0;
322 Log log;
323 // Copy "global" values from |log_|. Don't use |log_| directly since this
324 // function is const.
325 log.CopyFrom(log_);
327 for (AggregatedSocketEventLogMap::const_iterator it =
328 aggregated_socket_events_.begin();
329 it != aggregated_socket_events_.end();
330 ++it) {
331 AggregatedSocketEvent* new_aggregated_socket_event =
332 log.add_aggregated_socket_event();
333 new_aggregated_socket_event->CopyFrom(it->second->aggregated_socket_event);
335 const std::deque<SocketEvent>& socket_events = it->second->socket_events;
336 for (std::deque<SocketEvent>::const_iterator socket_event_it =
337 socket_events.begin();
338 socket_event_it != socket_events.end();
339 ++socket_event_it) {
340 SocketEvent* socket_event =
341 new_aggregated_socket_event->add_socket_event();
342 socket_event->CopyFrom(*socket_event_it);
346 std::string serialized;
347 if (!log.SerializeToString(&serialized)) {
348 VLOG(2) << "Failed to serialized proto to string.";
349 return scoped_ptr<char[]>();
352 return Compress(serialized, length);
355 void Logger::Reset() {
356 aggregated_socket_events_.clear();
357 log_.Clear();
360 LastErrors Logger::GetLastErrors(int channel_id) const {
361 AggregatedSocketEventLogMap::const_iterator it =
362 aggregated_socket_events_.find(channel_id);
363 if (it != aggregated_socket_events_.end()) {
364 return it->second->last_errors;
365 } else {
366 return LastErrors();
370 } // namespace cast_channel
371 } // namespace core_api
372 } // namespace extensions