Imported Upstream version 1.21.0
[platform/upstream/grpc.git] / src / core / lib / transport / metadata.h
1 /*
2  *
3  * Copyright 2015 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_CORE_LIB_TRANSPORT_METADATA_H
20 #define GRPC_CORE_LIB_TRANSPORT_METADATA_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include "include/grpc/impl/codegen/log.h"
25
26 #include <grpc/grpc.h>
27 #include <grpc/slice.h>
28
29 #include "src/core/lib/debug/trace.h"
30 #include "src/core/lib/gpr/useful.h"
31 #include "src/core/lib/gprpp/atomic.h"
32 #include "src/core/lib/gprpp/sync.h"
33
34 extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
35
36 /* This file provides a mechanism for tracking metadata through the grpc stack.
37    It's not intended for consumption outside of the library.
38
39    Metadata is tracked in the context of a grpc_mdctx. For the time being there
40    is one of these per-channel, avoiding cross channel interference with memory
41    use and lock contention.
42
43    The context tracks unique strings (grpc_mdstr) and pairs of strings
44    (grpc_mdelem). Any of these objects can be checked for equality by comparing
45    their pointers. These objects are reference counted.
46
47    grpc_mdelem can additionally store a (non-NULL) user data pointer. This
48    pointer is intended to be used to cache semantic meaning of a metadata
49    element. For example, an OAuth token may cache the credentials it represents
50    and the time at which it expires in the mdelem user data.
51
52    Combining this metadata cache and the hpack compression table allows us to
53    simply lookup complete preparsed objects quickly, incurring a few atomic
54    ops per metadata element on the fast path.
55
56    grpc_mdelem instances MAY live longer than their refcount implies, and are
57    garbage collected periodically, meaning cached data can easily outlive a
58    single request.
59
60    STATIC METADATA: in static_metadata.h we declare a set of static metadata.
61    These mdelems and mdstrs are available via pre-declared code generated macros
62    and are available to code anywhere between grpc_init() and grpc_shutdown().
63    They are not refcounted, but can be passed to _ref and _unref functions
64    declared here - in which case those functions are effectively no-ops. */
65
66 /* Forward declarations */
67 typedef struct grpc_mdelem grpc_mdelem;
68
69 /* if changing this, make identical changes in:
70    - grpc_core::{InternedMetadata, AllocatedMetadata}
71    - grpc_metadata in grpc_types.h */
72 typedef struct grpc_mdelem_data {
73   const grpc_slice key;
74   const grpc_slice value;
75   /* there is a private part to this in metadata.c */
76 } grpc_mdelem_data;
77
78 /* GRPC_MDELEM_STORAGE_* enum values that can be treated as interned always have
79    this bit set in their integer value */
80 #define GRPC_MDELEM_STORAGE_INTERNED_BIT 1
81
82 typedef enum {
83   /* memory pointed to by grpc_mdelem::payload is owned by an external system */
84   GRPC_MDELEM_STORAGE_EXTERNAL = 0,
85   /* memory pointed to by grpc_mdelem::payload is interned by the metadata
86      system */
87   GRPC_MDELEM_STORAGE_INTERNED = GRPC_MDELEM_STORAGE_INTERNED_BIT,
88   /* memory pointed to by grpc_mdelem::payload is allocated by the metadata
89      system */
90   GRPC_MDELEM_STORAGE_ALLOCATED = 2,
91   /* memory is in the static metadata table */
92   GRPC_MDELEM_STORAGE_STATIC = 2 | GRPC_MDELEM_STORAGE_INTERNED_BIT,
93 } grpc_mdelem_data_storage;
94
95 struct grpc_mdelem {
96   /* a grpc_mdelem_data* generally, with the two lower bits signalling memory
97      ownership as per grpc_mdelem_data_storage */
98   uintptr_t payload;
99 };
100
101 #define GRPC_MDELEM_DATA(md) ((grpc_mdelem_data*)((md).payload & ~(uintptr_t)3))
102 #define GRPC_MDELEM_STORAGE(md) \
103   ((grpc_mdelem_data_storage)((md).payload & (uintptr_t)3))
104 #ifdef __cplusplus
105 #define GRPC_MAKE_MDELEM(data, storage) \
106   (grpc_mdelem{((uintptr_t)(data)) | ((uintptr_t)storage)})
107 #else
108 #define GRPC_MAKE_MDELEM(data, storage) \
109   ((grpc_mdelem){((uintptr_t)(data)) | ((uintptr_t)storage)})
110 #endif
111 #define GRPC_MDELEM_IS_INTERNED(md)          \
112   ((grpc_mdelem_data_storage)((md).payload & \
113                               (uintptr_t)GRPC_MDELEM_STORAGE_INTERNED_BIT))
114
115 /* Unrefs the slices. */
116 grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key,
117                                     const grpc_slice& value);
118
119 /* Cheaply convert a grpc_metadata to a grpc_mdelem; may use the grpc_metadata
120    object as backing storage (so lifetimes should align) */
121 grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata);
122
123 /* Does not unref the slices; if a new non-interned mdelem is needed, allocates
124    one if compatible_external_backing_store is NULL, or uses
125    compatible_external_backing_store if it is non-NULL (in which case it's the
126    users responsibility to ensure that it outlives usage) */
127 grpc_mdelem grpc_mdelem_create(
128     const grpc_slice& key, const grpc_slice& value,
129     grpc_mdelem_data* compatible_external_backing_store);
130
131 #define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key)
132 #define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value)
133
134 bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b);
135 /* Often we compare metadata where we know a-priori that the second parameter is
136  * static, and that the keys match. This most commonly happens when processing
137  * metadata batch callouts in initial/trailing filters. In this case, fastpath
138  * grpc_mdelem_eq and remove unnecessary checks. */
139 inline bool grpc_mdelem_static_value_eq(grpc_mdelem a, grpc_mdelem b_static) {
140   if (a.payload == b_static.payload) return true;
141   return grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b_static));
142 }
143
144 /* Mutator and accessor for grpc_mdelem user data. The destructor function
145    is used as a type tag and is checked during user_data fetch. */
146 void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*if_destroy_func)(void*));
147 void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*),
148                                 void* data);
149
150 // Defined in metadata.cc.
151 struct mdtab_shard;
152
153 #ifndef NDEBUG
154 void grpc_mdelem_trace_ref(void* md, const grpc_slice& key,
155                            const grpc_slice& value, intptr_t refcnt,
156                            const char* file, int line);
157 void grpc_mdelem_trace_unref(void* md, const grpc_slice& key,
158                              const grpc_slice& value, intptr_t refcnt,
159                              const char* file, int line);
160 #endif
161 namespace grpc_core {
162
163 typedef void (*destroy_user_data_func)(void* data);
164
165 struct UserData {
166   Mutex mu_user_data;
167   grpc_core::Atomic<destroy_user_data_func> destroy_user_data;
168   grpc_core::Atomic<void*> data;
169 };
170
171 class InternedMetadata {
172  public:
173   struct BucketLink {
174     explicit BucketLink(InternedMetadata* md) : next(md) {}
175
176     InternedMetadata* next = nullptr;
177   };
178
179   InternedMetadata(const grpc_slice& key, const grpc_slice& value,
180                    uint32_t hash, InternedMetadata* next);
181   ~InternedMetadata();
182
183 #ifndef NDEBUG
184   void Ref(const char* file, int line) {
185     grpc_mdelem_trace_ref(this, key_, value_, RefValue(), file, line);
186     const intptr_t prior = refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
187     GPR_ASSERT(prior > 0);
188   }
189   bool Unref(const char* file, int line) {
190     grpc_mdelem_trace_unref(this, key_, value_, RefValue(), file, line);
191     return Unref();
192   }
193 #else
194   // We define a naked Ref() in the else-clause to make sure we don't
195   // inadvertently skip the assert on debug builds.
196   void Ref() {
197     /* we can assume the ref count is >= 1 as the application is calling
198        this function - meaning that no adjustment to mdtab_free is necessary,
199        simplifying the logic here to be just an atomic increment */
200     refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
201   }
202 #endif  // ifndef NDEBUG
203   bool Unref() {
204     const intptr_t prior = refcnt_.FetchSub(1, MemoryOrder::ACQ_REL);
205     GPR_DEBUG_ASSERT(prior > 0);
206     return prior == 1;
207   }
208
209   void RefWithShardLocked(mdtab_shard* shard);
210   const grpc_slice& key() const { return key_; }
211   const grpc_slice& value() const { return value_; }
212   UserData* user_data() { return &user_data_; }
213   uint32_t hash() { return hash_; }
214   InternedMetadata* bucket_next() { return link_.next; }
215   void set_bucket_next(InternedMetadata* md) { link_.next = md; }
216
217   static size_t CleanupLinkedMetadata(BucketLink* head);
218
219  private:
220   bool AllRefsDropped() { return refcnt_.Load(MemoryOrder::ACQUIRE) == 0; }
221   bool FirstRef() { return refcnt_.FetchAdd(1, MemoryOrder::RELAXED) == 0; }
222   intptr_t RefValue() { return refcnt_.Load(MemoryOrder::RELAXED); }
223
224   /* must be byte compatible with grpc_mdelem_data */
225   grpc_slice key_;
226   grpc_slice value_;
227
228   /* private only data */
229   grpc_core::Atomic<intptr_t> refcnt_;
230   uint32_t hash_;
231
232   UserData user_data_;
233
234   BucketLink link_;
235 };
236
237 /* Shadow structure for grpc_mdelem_data for allocated elements */
238 class AllocatedMetadata {
239  public:
240   AllocatedMetadata(const grpc_slice& key, const grpc_slice& value);
241   ~AllocatedMetadata();
242
243   const grpc_slice& key() const { return key_; }
244   const grpc_slice& value() const { return value_; }
245   UserData* user_data() { return &user_data_; }
246
247 #ifndef NDEBUG
248   void Ref(const char* file, int line) {
249     grpc_mdelem_trace_ref(this, key_, value_, RefValue(), file, line);
250     Ref();
251   }
252   bool Unref(const char* file, int line) {
253     grpc_mdelem_trace_unref(this, key_, value_, RefValue(), file, line);
254     return Unref();
255   }
256 #endif  // ifndef NDEBUG
257   void Ref() {
258     /* we can assume the ref count is >= 1 as the application is calling
259        this function - meaning that no adjustment to mdtab_free is necessary,
260        simplifying the logic here to be just an atomic increment */
261     refcnt_.FetchAdd(1, MemoryOrder::RELAXED);
262   }
263   bool Unref() {
264     const intptr_t prior = refcnt_.FetchSub(1, MemoryOrder::ACQ_REL);
265     GPR_DEBUG_ASSERT(prior > 0);
266     return prior == 1;
267   }
268
269  private:
270   intptr_t RefValue() { return refcnt_.Load(MemoryOrder::RELAXED); }
271
272   /* must be byte compatible with grpc_mdelem_data */
273   grpc_slice key_;
274   grpc_slice value_;
275
276   /* private only data */
277   grpc_core::Atomic<intptr_t> refcnt_;
278
279   UserData user_data_;
280 };
281
282 }  // namespace grpc_core
283
284 #ifndef NDEBUG
285 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__)
286 inline grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd, const char* file,
287                                    int line) {
288 #else  // ifndef NDEBUG
289 #define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s))
290 inline grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd) {
291 #endif  // ifndef NDEBUG
292   switch (GRPC_MDELEM_STORAGE(gmd)) {
293     case GRPC_MDELEM_STORAGE_EXTERNAL:
294     case GRPC_MDELEM_STORAGE_STATIC:
295       break;
296     case GRPC_MDELEM_STORAGE_INTERNED: {
297       auto* md =
298           reinterpret_cast<grpc_core::InternedMetadata*> GRPC_MDELEM_DATA(gmd);
299       /* use C assert to have this removed in opt builds */
300 #ifndef NDEBUG
301       md->Ref(file, line);
302 #else
303       md->Ref();
304 #endif
305       break;
306     }
307     case GRPC_MDELEM_STORAGE_ALLOCATED: {
308       auto* md =
309           reinterpret_cast<grpc_core::AllocatedMetadata*> GRPC_MDELEM_DATA(gmd);
310 #ifndef NDEBUG
311       md->Ref(file, line);
312 #else
313       md->Ref();
314 #endif
315       break;
316     }
317   }
318   return gmd;
319 }
320
321 #ifndef NDEBUG
322 #define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __LINE__)
323 void grpc_mdelem_do_unref(grpc_mdelem gmd, const char* file, int line);
324 inline void grpc_mdelem_unref(grpc_mdelem gmd, const char* file, int line) {
325 #else
326 #define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s))
327 void grpc_mdelem_do_unref(grpc_mdelem gmd);
328 inline void grpc_mdelem_unref(grpc_mdelem gmd) {
329 #endif
330   switch (GRPC_MDELEM_STORAGE(gmd)) {
331     case GRPC_MDELEM_STORAGE_EXTERNAL:
332     case GRPC_MDELEM_STORAGE_STATIC:
333       return;
334     case GRPC_MDELEM_STORAGE_INTERNED:
335     case GRPC_MDELEM_STORAGE_ALLOCATED:
336 #ifndef NDEBUG
337       grpc_mdelem_do_unref(gmd, file, line);
338 #else
339       grpc_mdelem_do_unref(gmd);
340 #endif
341       return;
342   }
343 }
344
345 #define GRPC_MDNULL GRPC_MAKE_MDELEM(NULL, GRPC_MDELEM_STORAGE_EXTERNAL)
346 #define GRPC_MDISNULL(md) (GRPC_MDELEM_DATA(md) == NULL)
347
348 /* We add 32 bytes of padding as per RFC-7540 section 6.5.2. */
349 #define GRPC_MDELEM_LENGTH(e)                                                  \
350   (GRPC_SLICE_LENGTH(GRPC_MDKEY((e))) + GRPC_SLICE_LENGTH(GRPC_MDVALUE((e))) + \
351    32)
352
353 #define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
354
355 void grpc_mdctx_global_init(void);
356 void grpc_mdctx_global_shutdown();
357
358 #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_H */