• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/transport/metadata_batch.h"
22 
23 #include <stdbool.h>
24 #include <string.h>
25 
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 
29 #include "src/core/lib/profiling/timers.h"
30 #include "src/core/lib/slice/slice_internal.h"
31 #include "src/core/lib/slice/slice_string_helpers.h"
32 
assert_valid_list(grpc_mdelem_list * list)33 static void assert_valid_list(grpc_mdelem_list* list) {
34 #ifndef NDEBUG
35   grpc_linked_mdelem* l;
36 
37   GPR_ASSERT((list->head == nullptr) == (list->tail == nullptr));
38   if (!list->head) return;
39   GPR_ASSERT(list->head->prev == nullptr);
40   GPR_ASSERT(list->tail->next == nullptr);
41   GPR_ASSERT((list->head == list->tail) == (list->head->next == nullptr));
42 
43   size_t verified_count = 0;
44   for (l = list->head; l; l = l->next) {
45     GPR_ASSERT(!GRPC_MDISNULL(l->md));
46     GPR_ASSERT((l->prev == nullptr) == (l == list->head));
47     GPR_ASSERT((l->next == nullptr) == (l == list->tail));
48     if (l->next) GPR_ASSERT(l->next->prev == l);
49     if (l->prev) GPR_ASSERT(l->prev->next == l);
50     verified_count++;
51   }
52   GPR_ASSERT(list->count == verified_count);
53 #else
54   // Avoid unused-parameter warning for debug-only parameter
55   (void)list;
56 #endif /* NDEBUG */
57 }
58 
assert_valid_callouts(grpc_metadata_batch * batch)59 static void assert_valid_callouts(grpc_metadata_batch* batch) {
60 #ifndef NDEBUG
61   for (grpc_linked_mdelem* l = batch->list.head; l != nullptr; l = l->next) {
62     grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md));
63     grpc_metadata_batch_callouts_index callout_idx =
64         GRPC_BATCH_INDEX_OF(key_interned);
65     if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) {
66       GPR_ASSERT(batch->idx.array[callout_idx] == l);
67     }
68     grpc_slice_unref_internal(key_interned);
69   }
70 #else
71   // Avoid unused-parameter warning for debug-only parameter
72   (void)batch;
73 #endif
74 }
75 
76 #ifndef NDEBUG
grpc_metadata_batch_assert_ok(grpc_metadata_batch * batch)77 void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) {
78   assert_valid_list(&batch->list);
79 }
80 #endif /* NDEBUG */
81 
grpc_metadata_batch_init(grpc_metadata_batch * batch)82 void grpc_metadata_batch_init(grpc_metadata_batch* batch) {
83   memset(batch, 0, sizeof(*batch));
84   batch->deadline = GRPC_MILLIS_INF_FUTURE;
85 }
86 
grpc_metadata_batch_destroy(grpc_metadata_batch * batch)87 void grpc_metadata_batch_destroy(grpc_metadata_batch* batch) {
88   grpc_linked_mdelem* l;
89   for (l = batch->list.head; l; l = l->next) {
90     GRPC_MDELEM_UNREF(l->md);
91   }
92 }
93 
grpc_attach_md_to_error(grpc_error * src,grpc_mdelem md)94 grpc_error* grpc_attach_md_to_error(grpc_error* src, grpc_mdelem md) {
95   grpc_error* out = grpc_error_set_str(
96       grpc_error_set_str(src, GRPC_ERROR_STR_KEY,
97                          grpc_slice_ref_internal(GRPC_MDKEY(md))),
98       GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md)));
99   return out;
100 }
101 
error_with_md(grpc_mdelem md)102 static grpc_error* GPR_ATTRIBUTE_NOINLINE error_with_md(grpc_mdelem md) {
103   return grpc_attach_md_to_error(
104       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), md);
105 }
106 
link_callout(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_metadata_batch_callouts_index idx)107 static grpc_error* link_callout(grpc_metadata_batch* batch,
108                                 grpc_linked_mdelem* storage,
109                                 grpc_metadata_batch_callouts_index idx) {
110   GPR_DEBUG_ASSERT(idx >= 0 && idx < GRPC_BATCH_CALLOUTS_COUNT);
111   if (GPR_LIKELY(batch->idx.array[idx] == nullptr)) {
112     ++batch->list.default_count;
113     batch->idx.array[idx] = storage;
114     return GRPC_ERROR_NONE;
115   }
116   return error_with_md(storage->md);
117 }
118 
119 static grpc_error* maybe_link_callout(grpc_metadata_batch* batch,
120                                       grpc_linked_mdelem* storage)
121     GRPC_MUST_USE_RESULT;
122 
maybe_link_callout(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)123 static grpc_error* maybe_link_callout(grpc_metadata_batch* batch,
124                                       grpc_linked_mdelem* storage) {
125   grpc_metadata_batch_callouts_index idx =
126       GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
127   if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
128     return GRPC_ERROR_NONE;
129   }
130   return link_callout(batch, storage, idx);
131 }
132 
maybe_unlink_callout(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)133 static void maybe_unlink_callout(grpc_metadata_batch* batch,
134                                  grpc_linked_mdelem* storage) {
135   grpc_metadata_batch_callouts_index idx =
136       GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
137   if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
138     return;
139   }
140   --batch->list.default_count;
141   GPR_DEBUG_ASSERT(batch->idx.array[idx] != nullptr);
142   batch->idx.array[idx] = nullptr;
143 }
144 
grpc_metadata_batch_add_head(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_mdelem elem_to_add)145 grpc_error* grpc_metadata_batch_add_head(grpc_metadata_batch* batch,
146                                          grpc_linked_mdelem* storage,
147                                          grpc_mdelem elem_to_add) {
148   GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add));
149   storage->md = elem_to_add;
150   return grpc_metadata_batch_link_head(batch, storage);
151 }
152 
link_head(grpc_mdelem_list * list,grpc_linked_mdelem * storage)153 static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
154   assert_valid_list(list);
155   GPR_DEBUG_ASSERT(!GRPC_MDISNULL(storage->md));
156   storage->prev = nullptr;
157   storage->next = list->head;
158   storage->reserved = nullptr;
159   if (list->head != nullptr) {
160     list->head->prev = storage;
161   } else {
162     list->tail = storage;
163   }
164   list->head = storage;
165   list->count++;
166   assert_valid_list(list);
167 }
168 
grpc_metadata_batch_link_head(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)169 grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch,
170                                           grpc_linked_mdelem* storage) {
171   assert_valid_callouts(batch);
172   grpc_error* err = maybe_link_callout(batch, storage);
173   if (err != GRPC_ERROR_NONE) {
174     assert_valid_callouts(batch);
175     return err;
176   }
177   link_head(&batch->list, storage);
178   assert_valid_callouts(batch);
179   return GRPC_ERROR_NONE;
180 }
181 
182 // TODO(arjunroy): Need to revisit this and see what guarantees exist between
183 // C-core and the internal-metadata subsystem. E.g. can we ensure a particular
184 // metadata is never added twice, even in the presence of user supplied data?
grpc_metadata_batch_link_head(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_metadata_batch_callouts_index idx)185 grpc_error* grpc_metadata_batch_link_head(
186     grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
187     grpc_metadata_batch_callouts_index idx) {
188   GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx);
189   assert_valid_callouts(batch);
190   grpc_error* err = link_callout(batch, storage, idx);
191   if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) {
192     assert_valid_callouts(batch);
193     return err;
194   }
195   link_head(&batch->list, storage);
196   assert_valid_callouts(batch);
197   return GRPC_ERROR_NONE;
198 }
199 
grpc_metadata_batch_add_tail(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_mdelem elem_to_add)200 grpc_error* grpc_metadata_batch_add_tail(grpc_metadata_batch* batch,
201                                          grpc_linked_mdelem* storage,
202                                          grpc_mdelem elem_to_add) {
203   GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add));
204   storage->md = elem_to_add;
205   return grpc_metadata_batch_link_tail(batch, storage);
206 }
207 
link_tail(grpc_mdelem_list * list,grpc_linked_mdelem * storage)208 static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
209   assert_valid_list(list);
210   GPR_DEBUG_ASSERT(!GRPC_MDISNULL(storage->md));
211   storage->prev = list->tail;
212   storage->next = nullptr;
213   storage->reserved = nullptr;
214   if (list->tail != nullptr) {
215     list->tail->next = storage;
216   } else {
217     list->head = storage;
218   }
219   list->tail = storage;
220   list->count++;
221   assert_valid_list(list);
222 }
223 
grpc_metadata_batch_link_tail(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)224 grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch,
225                                           grpc_linked_mdelem* storage) {
226   assert_valid_callouts(batch);
227   grpc_error* err = maybe_link_callout(batch, storage);
228   if (err != GRPC_ERROR_NONE) {
229     assert_valid_callouts(batch);
230     return err;
231   }
232   link_tail(&batch->list, storage);
233   assert_valid_callouts(batch);
234   return GRPC_ERROR_NONE;
235 }
236 
grpc_metadata_batch_link_tail(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_metadata_batch_callouts_index idx)237 grpc_error* grpc_metadata_batch_link_tail(
238     grpc_metadata_batch* batch, grpc_linked_mdelem* storage,
239     grpc_metadata_batch_callouts_index idx) {
240   GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx);
241   assert_valid_callouts(batch);
242   grpc_error* err = link_callout(batch, storage, idx);
243   if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) {
244     assert_valid_callouts(batch);
245     return err;
246   }
247   link_tail(&batch->list, storage);
248   assert_valid_callouts(batch);
249   return GRPC_ERROR_NONE;
250 }
251 
unlink_storage(grpc_mdelem_list * list,grpc_linked_mdelem * storage)252 static void unlink_storage(grpc_mdelem_list* list,
253                            grpc_linked_mdelem* storage) {
254   assert_valid_list(list);
255   if (storage->prev != nullptr) {
256     storage->prev->next = storage->next;
257   } else {
258     list->head = storage->next;
259   }
260   if (storage->next != nullptr) {
261     storage->next->prev = storage->prev;
262   } else {
263     list->tail = storage->prev;
264   }
265   list->count--;
266   assert_valid_list(list);
267 }
268 
grpc_metadata_batch_remove(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)269 void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
270                                 grpc_linked_mdelem* storage) {
271   assert_valid_callouts(batch);
272   maybe_unlink_callout(batch, storage);
273   unlink_storage(&batch->list, storage);
274   GRPC_MDELEM_UNREF(storage->md);
275   assert_valid_callouts(batch);
276 }
277 
grpc_metadata_batch_remove(grpc_metadata_batch * batch,grpc_metadata_batch_callouts_index idx)278 void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
279                                 grpc_metadata_batch_callouts_index idx) {
280   assert_valid_callouts(batch);
281   grpc_linked_mdelem* storage = batch->idx.array[idx];
282   GPR_DEBUG_ASSERT(storage != nullptr);
283   --batch->list.default_count;
284   batch->idx.array[idx] = nullptr;
285   unlink_storage(&batch->list, storage);
286   GRPC_MDELEM_UNREF(storage->md);
287   assert_valid_callouts(batch);
288 }
289 
grpc_metadata_batch_set_value(grpc_linked_mdelem * storage,const grpc_slice & value)290 void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
291                                    const grpc_slice& value) {
292   grpc_mdelem old_mdelem = storage->md;
293   grpc_mdelem new_mdelem = grpc_mdelem_from_slices(
294       grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value);
295   storage->md = new_mdelem;
296   GRPC_MDELEM_UNREF(old_mdelem);
297 }
298 
grpc_metadata_batch_substitute(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_mdelem new_mdelem)299 grpc_error* grpc_metadata_batch_substitute(grpc_metadata_batch* batch,
300                                            grpc_linked_mdelem* storage,
301                                            grpc_mdelem new_mdelem) {
302   assert_valid_callouts(batch);
303   grpc_error* error = GRPC_ERROR_NONE;
304   grpc_mdelem old_mdelem = storage->md;
305   if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) {
306     maybe_unlink_callout(batch, storage);
307     storage->md = new_mdelem;
308     error = maybe_link_callout(batch, storage);
309     if (error != GRPC_ERROR_NONE) {
310       unlink_storage(&batch->list, storage);
311       GRPC_MDELEM_UNREF(storage->md);
312     }
313   } else {
314     storage->md = new_mdelem;
315   }
316   GRPC_MDELEM_UNREF(old_mdelem);
317   assert_valid_callouts(batch);
318   return error;
319 }
320 
grpc_metadata_batch_clear(grpc_metadata_batch * batch)321 void grpc_metadata_batch_clear(grpc_metadata_batch* batch) {
322   grpc_metadata_batch_destroy(batch);
323   grpc_metadata_batch_init(batch);
324 }
325 
grpc_metadata_batch_is_empty(grpc_metadata_batch * batch)326 bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch) {
327   return batch->list.head == nullptr &&
328          batch->deadline == GRPC_MILLIS_INF_FUTURE;
329 }
330 
grpc_metadata_batch_size(grpc_metadata_batch * batch)331 size_t grpc_metadata_batch_size(grpc_metadata_batch* batch) {
332   size_t size = 0;
333   for (grpc_linked_mdelem* elem = batch->list.head; elem != nullptr;
334        elem = elem->next) {
335     size += GRPC_MDELEM_LENGTH(elem->md);
336   }
337   return size;
338 }
339 
add_error(grpc_error ** composite,grpc_error * error,const char * composite_error_string)340 static void add_error(grpc_error** composite, grpc_error* error,
341                       const char* composite_error_string) {
342   if (error == GRPC_ERROR_NONE) return;
343   if (*composite == GRPC_ERROR_NONE) {
344     *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string);
345   }
346   *composite = grpc_error_add_child(*composite, error);
347 }
348 
grpc_metadata_batch_filter(grpc_metadata_batch * batch,grpc_metadata_batch_filter_func func,void * user_data,const char * composite_error_string)349 grpc_error* grpc_metadata_batch_filter(grpc_metadata_batch* batch,
350                                        grpc_metadata_batch_filter_func func,
351                                        void* user_data,
352                                        const char* composite_error_string) {
353   grpc_linked_mdelem* l = batch->list.head;
354   grpc_error* error = GRPC_ERROR_NONE;
355   while (l) {
356     grpc_linked_mdelem* next = l->next;
357     grpc_filtered_mdelem new_mdelem = func(user_data, l->md);
358     add_error(&error, new_mdelem.error, composite_error_string);
359     if (GRPC_MDISNULL(new_mdelem.md)) {
360       grpc_metadata_batch_remove(batch, l);
361     } else if (new_mdelem.md.payload != l->md.payload) {
362       grpc_metadata_batch_substitute(batch, l, new_mdelem.md);
363     }
364     l = next;
365   }
366   return error;
367 }
368 
grpc_metadata_batch_copy(grpc_metadata_batch * src,grpc_metadata_batch * dst,grpc_linked_mdelem * storage)369 void grpc_metadata_batch_copy(grpc_metadata_batch* src,
370                               grpc_metadata_batch* dst,
371                               grpc_linked_mdelem* storage) {
372   grpc_metadata_batch_init(dst);
373   dst->deadline = src->deadline;
374   size_t i = 0;
375   for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr;
376        elem = elem->next) {
377     // Error unused in non-debug builds.
378     grpc_error* GRPC_UNUSED error = grpc_metadata_batch_add_tail(
379         dst, &storage[i++], GRPC_MDELEM_REF(elem->md));
380     // The only way that grpc_metadata_batch_add_tail() can fail is if
381     // there's a duplicate entry for a callout.  However, that can't be
382     // the case here, because we would not have been allowed to create
383     // a source batch that had that kind of conflict.
384     GPR_DEBUG_ASSERT(error == GRPC_ERROR_NONE);
385   }
386 }
387 
grpc_metadata_batch_move(grpc_metadata_batch * src,grpc_metadata_batch * dst)388 void grpc_metadata_batch_move(grpc_metadata_batch* src,
389                               grpc_metadata_batch* dst) {
390   *dst = *src;
391   grpc_metadata_batch_init(src);
392 }
393