1 /* 2 * 3 * Copyright 2018 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 #ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H 20 #define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <string.h> 25 26 #include "absl/base/internal/endian.h" 27 #include "absl/strings/string_view.h" 28 #include "opencensus/trace/span_context.h" 29 #include "opencensus/trace/span_id.h" 30 #include "opencensus/trace/trace_id.h" 31 32 namespace grpc { 33 34 // TODO: Rename to GrpcTraceContextV0. 35 struct GrpcTraceContext { GrpcTraceContextGrpcTraceContext36 GrpcTraceContext() {} 37 GrpcTraceContextGrpcTraceContext38 explicit GrpcTraceContext(const ::opencensus::trace::SpanContext& ctx) { 39 ctx.trace_id().CopyTo(trace_id); 40 ctx.span_id().CopyTo(span_id); 41 ctx.trace_options().CopyTo(trace_options); 42 } 43 ToSpanContextGrpcTraceContext44 ::opencensus::trace::SpanContext ToSpanContext() const { 45 return ::opencensus::trace::SpanContext( 46 ::opencensus::trace::TraceId(trace_id), 47 ::opencensus::trace::SpanId(span_id), 48 ::opencensus::trace::TraceOptions(trace_options)); 49 } 50 51 // TODO: For performance: 52 // uint8_t version; 53 // uint8_t trace_id_field_id; 54 uint8_t trace_id[::opencensus::trace::TraceId::kSize]; 55 // uint8_t span_id_field_id; 56 uint8_t span_id[::opencensus::trace::SpanId::kSize]; 57 // uint8_t trace_options_field_id; 58 uint8_t trace_options[::opencensus::trace::TraceOptions::kSize]; 59 }; 60 61 // TraceContextEncoding encapsulates the logic for encoding and decoding of 62 // trace contexts. 63 class TraceContextEncoding { 64 public: 65 // Size of encoded GrpcTraceContext. (16 + 8 + 1 + 4) 66 static constexpr size_t kGrpcTraceContextSize = 29; 67 // Error value. 68 static constexpr size_t kEncodeDecodeFailure = 0; 69 70 // Deserializes a GrpcTraceContext from the incoming buffer. Returns the 71 // number of bytes deserialized from the buffer. If the incoming buffer is 72 // empty or the encoding version is not supported it will return 0 bytes, 73 // currently only version 0 is supported. If an unknown field ID is 74 // encountered it will return immediately without parsing the rest of the 75 // buffer. Inlined for performance reasons. Decode(absl::string_view buf,GrpcTraceContext * tc)76 static size_t Decode(absl::string_view buf, GrpcTraceContext* tc) { 77 if (buf.empty()) { 78 return kEncodeDecodeFailure; 79 } 80 uint8_t version = buf[kVersionIdOffset]; 81 // TODO: Support other versions later. Only support version 0 for 82 // now. 83 if (version != kVersionId) { 84 return kEncodeDecodeFailure; 85 } 86 87 size_t pos = kVersionIdSize; 88 while (pos < buf.size()) { 89 size_t bytes_read = 90 ParseField(absl::string_view(&buf[pos], buf.size() - pos), tc); 91 if (bytes_read == 0) { 92 break; 93 } else { 94 pos += bytes_read; 95 } 96 } 97 return pos; 98 } 99 100 // Serializes a GrpcTraceContext into the provided buffer. Returns the number 101 // of bytes serialized into the buffer. If the buffer is not of sufficient 102 // size (it must be at least kGrpcTraceContextSize bytes) it will drop 103 // everything and return 0 bytes serialized. Inlined for performance reasons. Encode(const GrpcTraceContext & tc,char * buf,size_t buf_size)104 static size_t Encode(const GrpcTraceContext& tc, char* buf, size_t buf_size) { 105 if (buf_size < kGrpcTraceContextSize) { 106 return kEncodeDecodeFailure; 107 } 108 buf[kVersionIdOffset] = kVersionId; 109 buf[kTraceIdOffset] = kTraceIdField; 110 memcpy(&buf[kTraceIdOffset + 1], tc.trace_id, 111 opencensus::trace::TraceId::kSize); 112 buf[kSpanIdOffset] = kSpanIdField; 113 memcpy(&buf[kSpanIdOffset + 1], tc.span_id, 114 opencensus::trace::SpanId::kSize); 115 buf[kTraceOptionsOffset] = kTraceOptionsField; 116 memcpy(&buf[kTraceOptionsOffset + 1], tc.trace_options, 117 opencensus::trace::TraceOptions::kSize); 118 return kGrpcTraceContextSize; 119 } 120 121 private: 122 // Parses the next field from the incoming buffer and stores the parsed value 123 // in a GrpcTraceContext struct. If it does not recognize the field ID it 124 // will return 0, otherwise it returns the number of bytes read. ParseField(absl::string_view buf,GrpcTraceContext * tc)125 static size_t ParseField(absl::string_view buf, GrpcTraceContext* tc) { 126 // TODO: Add support for multi-byte field IDs. 127 if (buf.empty()) { 128 return 0; 129 } 130 // Field ID is always the first byte in a field. 131 uint32_t field_id = buf[0]; 132 size_t bytes_read = kFieldIdSize; 133 switch (field_id) { 134 case kTraceIdField: 135 bytes_read += kTraceIdSize; 136 if (bytes_read > buf.size()) { 137 return 0; 138 } 139 memcpy(tc->trace_id, &buf[kFieldIdSize], 140 opencensus::trace::TraceId::kSize); 141 break; 142 case kSpanIdField: 143 bytes_read += kSpanIdSize; 144 if (bytes_read > buf.size()) { 145 return 0; 146 } 147 memcpy(tc->span_id, &buf[kFieldIdSize], 148 opencensus::trace::SpanId::kSize); 149 break; 150 case kTraceOptionsField: 151 bytes_read += kTraceOptionsSize; 152 if (bytes_read > buf.size()) { 153 return 0; 154 } 155 memcpy(tc->trace_options, &buf[kFieldIdSize], 156 opencensus::trace::TraceOptions::kSize); 157 break; 158 default: // Invalid field ID 159 return 0; 160 } 161 162 return bytes_read; 163 } 164 165 // Size of Version ID. 166 static constexpr size_t kVersionIdSize = 1; 167 // Size of Field ID. 168 static constexpr size_t kFieldIdSize = 1; 169 170 // Offset and value for currently supported version ID. 171 static constexpr size_t kVersionIdOffset = 0; 172 static constexpr size_t kVersionId = 0; 173 174 // Fixed Field ID values: 175 enum FieldIdValue { 176 kTraceIdField = 0, 177 kSpanIdField = 1, 178 kTraceOptionsField = 2, 179 }; 180 181 // Field data sizes in bytes 182 enum FieldSize { 183 kTraceIdSize = 16, 184 kSpanIdSize = 8, 185 kTraceOptionsSize = 1, 186 }; 187 188 // Fixed size offsets for field ID start positions during encoding. Field 189 // data immediately follows. 190 enum FieldIdOffset { 191 kTraceIdOffset = kVersionIdSize, 192 kSpanIdOffset = kTraceIdOffset + kFieldIdSize + kTraceIdSize, 193 kTraceOptionsOffset = kSpanIdOffset + kFieldIdSize + kSpanIdSize, 194 }; 195 196 TraceContextEncoding() = delete; 197 TraceContextEncoding(const TraceContextEncoding&) = delete; 198 TraceContextEncoding(TraceContextEncoding&&) = delete; 199 TraceContextEncoding operator=(const TraceContextEncoding&) = delete; 200 TraceContextEncoding operator=(TraceContextEncoding&&) = delete; 201 }; 202 203 // TODO: This may not be needed. Check to see if opencensus requires 204 // a trailing server response. 205 // RpcServerStatsEncoding encapsulates the logic for encoding and decoding of 206 // rpc server stats messages. Rpc server stats consists of a uint64_t time 207 // value (server latency in nanoseconds). 208 class RpcServerStatsEncoding { 209 public: 210 // Size of encoded RPC server stats. 211 static constexpr size_t kRpcServerStatsSize = 10; 212 // Error value. 213 static constexpr size_t kEncodeDecodeFailure = 0; 214 215 // Deserializes rpc server stats from the incoming 'buf' into *time. Returns 216 // number of bytes decoded. If the buffer is of insufficient size (it must be 217 // at least kRpcServerStatsSize bytes) or the encoding version or field ID are 218 // unrecognized, *time will be set to 0 and it will return 219 // kEncodeDecodeFailure. Inlined for performance reasons. Decode(absl::string_view buf,uint64_t * time)220 static size_t Decode(absl::string_view buf, uint64_t* time) { 221 if (buf.size() < kRpcServerStatsSize) { 222 *time = 0; 223 return kEncodeDecodeFailure; 224 } 225 226 uint8_t version = buf[kVersionIdOffset]; 227 uint32_t fieldID = buf[kServerElapsedTimeOffset]; 228 if (version != kVersionId || fieldID != kServerElapsedTimeField) { 229 *time = 0; 230 return kEncodeDecodeFailure; 231 } 232 *time = absl::little_endian::Load64( 233 &buf[kServerElapsedTimeOffset + kFieldIdSize]); 234 return kRpcServerStatsSize; 235 } 236 237 // Serializes rpc server stats into the provided buffer. It returns the 238 // number of bytes written to the buffer. If the buffer is smaller than 239 // kRpcServerStatsSize bytes it will return kEncodeDecodeFailure. Inlined for 240 // performance reasons. Encode(uint64_t time,char * buf,size_t buf_size)241 static size_t Encode(uint64_t time, char* buf, size_t buf_size) { 242 if (buf_size < kRpcServerStatsSize) { 243 return kEncodeDecodeFailure; 244 } 245 246 buf[kVersionIdOffset] = kVersionId; 247 buf[kServerElapsedTimeOffset] = kServerElapsedTimeField; 248 absl::little_endian::Store64(&buf[kServerElapsedTimeOffset + kFieldIdSize], 249 time); 250 return kRpcServerStatsSize; 251 } 252 253 private: 254 // Size of Version ID. 255 static constexpr size_t kVersionIdSize = 1; 256 // Size of Field ID. 257 static constexpr size_t kFieldIdSize = 1; 258 259 // Offset and value for currently supported version ID. 260 static constexpr size_t kVersionIdOffset = 0; 261 static constexpr size_t kVersionId = 0; 262 263 enum FieldIdValue { 264 kServerElapsedTimeField = 0, 265 }; 266 267 enum FieldSize { 268 kServerElapsedTimeSize = 8, 269 }; 270 271 enum FieldIdOffset { 272 kServerElapsedTimeOffset = kVersionIdSize, 273 }; 274 275 RpcServerStatsEncoding() = delete; 276 RpcServerStatsEncoding(const RpcServerStatsEncoding&) = delete; 277 RpcServerStatsEncoding(RpcServerStatsEncoding&&) = delete; 278 RpcServerStatsEncoding operator=(const RpcServerStatsEncoding&) = delete; 279 RpcServerStatsEncoding operator=(RpcServerStatsEncoding&&) = delete; 280 }; 281 282 } // namespace grpc 283 284 #endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H */ 285