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