blob: e954626da8469598f1cd839a151a4e161d4e394b [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_TRAFFIC_ANNOTATION_NETWORK_TRAFFIC_ANNOTATION_H_
#define NET_TRAFFIC_ANNOTATION_NETWORK_TRAFFIC_ANNOTATION_H_
#include "base/logging.h"
#include "build/build_config.h"
namespace {
// Recursively compute hash code of the given string as a constant expression.
template <int N>
constexpr uint32_t recursive_hash(const char* str) {
return static_cast<uint32_t>((recursive_hash<N - 1>(str) * 31 + str[N - 1]) %
138003713);
}
// Recursion stopper for the above function. Note that string of size 0 will
// result in compile error.
template <>
constexpr uint32_t recursive_hash<1>(const char* str) {
return static_cast<uint32_t>(*str);
}
// Entry point to function that computes hash as constant expression.
#define COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(S) \
static_cast<int32_t>(recursive_hash<sizeof(S) - 1>(S))
constexpr int TRAFFIC_ANNOTATION_UNINITIALIZED = -1;
} // namespace
namespace net {
// Defined types for network traffic annotation tags.
struct NetworkTrafficAnnotationTag {
const int32_t unique_id_hash_code;
bool operator==(const NetworkTrafficAnnotationTag& other) const {
return unique_id_hash_code == other.unique_id_hash_code;
}
static NetworkTrafficAnnotationTag NotReached() {
NOTREACHED();
return net::NetworkTrafficAnnotationTag({TRAFFIC_ANNOTATION_UNINITIALIZED});
}
};
struct PartialNetworkTrafficAnnotationTag {
const int32_t unique_id_hash_code;
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
// |completing_id_hash_code| holds a reference to the hash coded unique id
// of a network traffic annotation (or group id of several network traffic
// annotations) that complete a partial network annotation. Please refer to
// the description of DefinePartialNetworkTrafficAnnotation function for more
// details.
// This value is used by the clang tools to find linkage between partial
// annotations and their completing parts, and is used in debug mode to check
// if an intended completing part is added to a partial network annotation.
const int32_t completing_id_hash_code;
#endif
};
// Function to convert a network traffic annotation's unique id and protobuf
// text into a NetworkTrafficAnnotationTag.
//
// This function serves as a tag that can be discovered and extracted via
// clang tools. This allows reviewing all network traffic that is generated
// and annotated by Chrome.
//
// |unique_id| should be a string that uniquely identifies this annotation
// across all of Chromium source code. |unique_id| should be kept unchanged
// as long as possible as its hashed value will be used for differnt logging,
// debugging, or auditing tasks. Unique ids should include only alphanumeric
// characters and underline.
// |proto| is a text-encoded NetworkTrafficAnnotation protobuf (see
// tools/traffic_annotation/traffic_annotation.proto)
//
// An empty and a sample template for the text-encoded protobuf can be found in
// tools/traffic_annotation/sample_traffic_annotation.cc.
// TODO(crbug.com/690323): Add tools to check annotation text's format during
// presubmit checks.
template <size_t N1, size_t N2>
constexpr NetworkTrafficAnnotationTag DefineNetworkTrafficAnnotation(
const char (&unique_id)[N1],
const char (&)[N2]) {
return NetworkTrafficAnnotationTag(
{COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(unique_id)});
}
// There are cases where the network traffic annotation cannot be fully
// specified in one place. For example, in one place we know the trigger of a
// network request and in another place we know the data that will be sent. In
// these cases, we prefer that both parts of the annotation appear in context so
// that they are updated if code changes. The following functions help splitting
// the network traffic annotation into two pieces. Please refer to
// tools/traffic_annotation/sample_traffic_annotation.cc for usage samples.
// This function can be used to define a partial annotation that will be
// completed later. The completing annotation can be defined with either of
// 'CompleteNetworkTrafficAnnotation' or
// 'BranchedCompleteNetworkTrafficAnnotation' functions. In case of
// CompleteNetworkTrafficAnnotation, |completing_id| is the unique id of the
// annotation that will complete it. In the case of
// BranchedCompleteNetworkTrafficAnnotation, |completing_id| is the group id
// of the completing annotations.
template <size_t N1, size_t N2, size_t N3>
constexpr PartialNetworkTrafficAnnotationTag
DefinePartialNetworkTrafficAnnotation(const char (&unique_id)[N1],
const char (&completing_id)[N2],
const char (&)[N3]) {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
return PartialNetworkTrafficAnnotationTag(
{COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(unique_id),
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(completing_id)});
#else
return PartialNetworkTrafficAnnotationTag(
{COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(unique_id)});
#endif
}
// This function can be used to define a completing partial annotation. This
// annotation adds details to another annotation that is defined before.
// |partial_annotation| is the PartialNetworkTrafficAnnotationTag returned
// by a call to DefinePartialNetworkTrafficAnnotation().
template <size_t N1, size_t N2>
NetworkTrafficAnnotationTag CompleteNetworkTrafficAnnotation(
const char (&unique_id)[N1],
const PartialNetworkTrafficAnnotationTag& partial_annotation,
const char (&)[N2]) {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
DCHECK(partial_annotation.completing_id_hash_code ==
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(unique_id) ||
partial_annotation.unique_id_hash_code ==
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH("test_partial") ||
partial_annotation.unique_id_hash_code ==
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH("undefined"));
#endif
return NetworkTrafficAnnotationTag({partial_annotation.unique_id_hash_code});
}
// This function can be used to define a completing partial annotation that is
// branched into several annotations. In this case, |group_id| is a common id
// that is used by all members of the branch and referenced by partial
// annotation that is completed by them.
template <size_t N1, size_t N2, size_t N3>
NetworkTrafficAnnotationTag BranchedCompleteNetworkTrafficAnnotation(
const char (&unique_id)[N1],
const char (&)[N2],
const PartialNetworkTrafficAnnotationTag& partial_annotation,
const char (&)[N3]) {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
DCHECK(partial_annotation.completing_id_hash_code ==
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(unique_id) ||
partial_annotation.unique_id_hash_code ==
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH("test_partial") ||
partial_annotation.unique_id_hash_code ==
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH("undefined"));
#endif
return NetworkTrafficAnnotationTag(
{COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(unique_id)});
}
// Example for joining N x 1 partial annotations:
// N functions foo1(), ..., fooN() call one function bar(). Each
// foo...() function defines part of a network traffic annotation.
// These N partial annotations are combined with a second part in
// bar().
//
// void foo1() {
// auto tag = DefinePartialNetworkTrafficAnnotation(
// "call_by_foo1", "completion_by_bar", [partial_proto]);
// bar(tag);
// }
// void foo2() {
// auto tag = DefinePartialNetworkTrafficAnnotation(
// "call_by_foo2", "completion_by_bar", [partial_proto]);
// bar(tag);
// }
// void bar(PartialNetworkTrafficAnnotationTag tag) {
// auto final_tag = CompleteNetworkTrafficAnnotation(
// "completion_by_bar", tag, [rest_of_proto]);
// // final_tag matches the value of tag (which is hash code of
// // "call_by_fooX" where X can be 1 or 2).
// net::URLFetcher::Create(..., final_tag);
// }
// Example for joining 1 x N partial annotations:
// A function foo() calls a function bar(bool param), that sends
// different network requests depending on param. Both functions
// define parts of the network traffic annotation.
//
// void foo(bool param) {
// auto tag = DefinePartialNetworkTrafficAnnotation(
// "call_by_foo1", "completion_by_bar", [partial_proto]);
// bar(param, tag);
// }
// void bar(bool param, PartialNetworkTrafficAnnotationTag tag) {
// if (param) {
// auto final_tag = BranchedCompleteNetworkTrafficAnnotation(
// "call_bool_branch_1", "completion_by_bar", tag, [rest_of_proto]);
// // final_tag is hash code of "call_bool_branch_1".
// net::URLFetcher::Create(url1, ..., final_tag);
// } else {
// auto final_tag = BranchedCompleteNetworkTrafficAnnotation(
// "call_bool_branch_2", "completion_by_bar", tag, [rest_of_proto]);
// // final_tag is hash code of "call_bool_branch_2".
// net::URLFetcher::Create(url2, ..., final_tag);
// }
// }
// Please do not use this unless uninitialized annotations are required.
// Mojo interfaces for this class and the next one are defined in
// '/services/network/public/mojom'.
struct MutableNetworkTrafficAnnotationTag {
MutableNetworkTrafficAnnotationTag()
: unique_id_hash_code(TRAFFIC_ANNOTATION_UNINITIALIZED) {}
explicit MutableNetworkTrafficAnnotationTag(
const NetworkTrafficAnnotationTag& traffic_annotation)
: unique_id_hash_code(traffic_annotation.unique_id_hash_code) {}
int32_t unique_id_hash_code;
bool operator==(const MutableNetworkTrafficAnnotationTag& other) const {
return unique_id_hash_code == other.unique_id_hash_code;
}
explicit operator NetworkTrafficAnnotationTag() const {
DCHECK(is_valid());
return NetworkTrafficAnnotationTag({unique_id_hash_code});
}
bool is_valid() const {
return unique_id_hash_code != TRAFFIC_ANNOTATION_UNINITIALIZED;
}
void reset() { unique_id_hash_code = TRAFFIC_ANNOTATION_UNINITIALIZED; }
};
struct MutablePartialNetworkTrafficAnnotationTag {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
MutablePartialNetworkTrafficAnnotationTag()
: unique_id_hash_code(TRAFFIC_ANNOTATION_UNINITIALIZED),
completing_id_hash_code(TRAFFIC_ANNOTATION_UNINITIALIZED) {}
explicit MutablePartialNetworkTrafficAnnotationTag(
const PartialNetworkTrafficAnnotationTag& partial_traffic_annotation)
: unique_id_hash_code(partial_traffic_annotation.unique_id_hash_code),
completing_id_hash_code(
partial_traffic_annotation.completing_id_hash_code) {}
int32_t unique_id_hash_code;
int32_t completing_id_hash_code;
explicit operator PartialNetworkTrafficAnnotationTag() const {
DCHECK(is_valid());
return PartialNetworkTrafficAnnotationTag(
{unique_id_hash_code, completing_id_hash_code});
}
bool is_valid() const {
return unique_id_hash_code != TRAFFIC_ANNOTATION_UNINITIALIZED &&
completing_id_hash_code != TRAFFIC_ANNOTATION_UNINITIALIZED;
}
void reset() {
unique_id_hash_code = TRAFFIC_ANNOTATION_UNINITIALIZED;
completing_id_hash_code = TRAFFIC_ANNOTATION_UNINITIALIZED;
}
#else
MutablePartialNetworkTrafficAnnotationTag()
: unique_id_hash_code(TRAFFIC_ANNOTATION_UNINITIALIZED) {}
explicit MutablePartialNetworkTrafficAnnotationTag(
const PartialNetworkTrafficAnnotationTag& partial_traffic_annotation)
: unique_id_hash_code(partial_traffic_annotation.unique_id_hash_code) {}
int32_t unique_id_hash_code;
explicit operator PartialNetworkTrafficAnnotationTag() const {
return PartialNetworkTrafficAnnotationTag({unique_id_hash_code});
}
bool is_valid() const {
return unique_id_hash_code != TRAFFIC_ANNOTATION_UNINITIALIZED;
}
void reset() { unique_id_hash_code = TRAFFIC_ANNOTATION_UNINITIALIZED; }
#endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
};
} // namespace net
// Placeholder for unannotated usages.
#if !defined(OS_WIN) && !defined(OS_LINUX) && !defined(OS_CHROMEOS)
#define TRAFFIC_ANNOTATION_WITHOUT_PROTO(ANNOTATION_ID) \
net::DefineNetworkTrafficAnnotation(ANNOTATION_ID, "No proto yet.")
#endif
#define NO_TRAFFIC_ANNOTATION_YET \
net::DefineNetworkTrafficAnnotation("undefined", "Nothing here yet.")
#define NO_PARTIAL_TRAFFIC_ANNOTATION_YET \
net::DefinePartialNetworkTrafficAnnotation("undefined", "undefined", \
"Nothing here yet.")
#define MISSING_TRAFFIC_ANNOTATION \
net::DefineNetworkTrafficAnnotation( \
"missing", "Function called without traffic annotation.")
#endif // NET_TRAFFIC_ANNOTATION_NETWORK_TRAFFIC_ANNOTATION_H_