• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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