• 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 #endif /* NDEBUG */
54 }
55 
assert_valid_callouts(grpc_metadata_batch * batch)56 static void assert_valid_callouts(grpc_metadata_batch* batch) {
57 #ifndef NDEBUG
58   for (grpc_linked_mdelem* l = batch->list.head; l != nullptr; l = l->next) {
59     grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md));
60     grpc_metadata_batch_callouts_index callout_idx =
61         GRPC_BATCH_INDEX_OF(key_interned);
62     if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) {
63       GPR_ASSERT(batch->idx.array[callout_idx] == l);
64     }
65     grpc_slice_unref_internal(key_interned);
66   }
67 #endif
68 }
69 
70 #ifndef NDEBUG
grpc_metadata_batch_assert_ok(grpc_metadata_batch * batch)71 void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) {
72   assert_valid_list(&batch->list);
73 }
74 #endif /* NDEBUG */
75 
grpc_metadata_batch_init(grpc_metadata_batch * batch)76 void grpc_metadata_batch_init(grpc_metadata_batch* batch) {
77   memset(batch, 0, sizeof(*batch));
78   batch->deadline = GRPC_MILLIS_INF_FUTURE;
79 }
80 
grpc_metadata_batch_destroy(grpc_metadata_batch * batch)81 void grpc_metadata_batch_destroy(grpc_metadata_batch* batch) {
82   grpc_linked_mdelem* l;
83   for (l = batch->list.head; l; l = l->next) {
84     GRPC_MDELEM_UNREF(l->md);
85   }
86 }
87 
grpc_attach_md_to_error(grpc_error * src,grpc_mdelem md)88 grpc_error* grpc_attach_md_to_error(grpc_error* src, grpc_mdelem md) {
89   grpc_error* out = grpc_error_set_str(
90       grpc_error_set_str(src, GRPC_ERROR_STR_KEY,
91                          grpc_slice_ref_internal(GRPC_MDKEY(md))),
92       GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md)));
93   return out;
94 }
95 
96 static grpc_error* maybe_link_callout(grpc_metadata_batch* batch,
97                                       grpc_linked_mdelem* storage)
98     GRPC_MUST_USE_RESULT;
99 
maybe_link_callout(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)100 static grpc_error* maybe_link_callout(grpc_metadata_batch* batch,
101                                       grpc_linked_mdelem* storage) {
102   grpc_metadata_batch_callouts_index idx =
103       GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
104   if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
105     return GRPC_ERROR_NONE;
106   }
107   if (batch->idx.array[idx] == nullptr) {
108     ++batch->list.default_count;
109     batch->idx.array[idx] = storage;
110     return GRPC_ERROR_NONE;
111   }
112   return grpc_attach_md_to_error(
113       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"),
114       storage->md);
115 }
116 
maybe_unlink_callout(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)117 static void maybe_unlink_callout(grpc_metadata_batch* batch,
118                                  grpc_linked_mdelem* storage) {
119   grpc_metadata_batch_callouts_index idx =
120       GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
121   if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
122     return;
123   }
124   --batch->list.default_count;
125   GPR_ASSERT(batch->idx.array[idx] != nullptr);
126   batch->idx.array[idx] = nullptr;
127 }
128 
grpc_metadata_batch_add_head(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_mdelem elem_to_add)129 grpc_error* grpc_metadata_batch_add_head(grpc_metadata_batch* batch,
130                                          grpc_linked_mdelem* storage,
131                                          grpc_mdelem elem_to_add) {
132   GPR_ASSERT(!GRPC_MDISNULL(elem_to_add));
133   storage->md = elem_to_add;
134   return grpc_metadata_batch_link_head(batch, storage);
135 }
136 
link_head(grpc_mdelem_list * list,grpc_linked_mdelem * storage)137 static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
138   assert_valid_list(list);
139   GPR_ASSERT(!GRPC_MDISNULL(storage->md));
140   storage->prev = nullptr;
141   storage->next = list->head;
142   if (list->head != nullptr) {
143     list->head->prev = storage;
144   } else {
145     list->tail = storage;
146   }
147   list->head = storage;
148   list->count++;
149   assert_valid_list(list);
150 }
151 
grpc_metadata_batch_link_head(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)152 grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch,
153                                           grpc_linked_mdelem* storage) {
154   assert_valid_callouts(batch);
155   grpc_error* err = maybe_link_callout(batch, storage);
156   if (err != GRPC_ERROR_NONE) {
157     assert_valid_callouts(batch);
158     return err;
159   }
160   link_head(&batch->list, storage);
161   assert_valid_callouts(batch);
162   return GRPC_ERROR_NONE;
163 }
164 
grpc_metadata_batch_add_tail(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_mdelem elem_to_add)165 grpc_error* grpc_metadata_batch_add_tail(grpc_metadata_batch* batch,
166                                          grpc_linked_mdelem* storage,
167                                          grpc_mdelem elem_to_add) {
168   GPR_ASSERT(!GRPC_MDISNULL(elem_to_add));
169   storage->md = elem_to_add;
170   return grpc_metadata_batch_link_tail(batch, storage);
171 }
172 
link_tail(grpc_mdelem_list * list,grpc_linked_mdelem * storage)173 static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) {
174   assert_valid_list(list);
175   GPR_ASSERT(!GRPC_MDISNULL(storage->md));
176   storage->prev = list->tail;
177   storage->next = nullptr;
178   storage->reserved = nullptr;
179   if (list->tail != nullptr) {
180     list->tail->next = storage;
181   } else {
182     list->head = storage;
183   }
184   list->tail = storage;
185   list->count++;
186   assert_valid_list(list);
187 }
188 
grpc_metadata_batch_link_tail(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)189 grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch,
190                                           grpc_linked_mdelem* storage) {
191   assert_valid_callouts(batch);
192   grpc_error* err = maybe_link_callout(batch, storage);
193   if (err != GRPC_ERROR_NONE) {
194     assert_valid_callouts(batch);
195     return err;
196   }
197   link_tail(&batch->list, storage);
198   assert_valid_callouts(batch);
199   return GRPC_ERROR_NONE;
200 }
201 
unlink_storage(grpc_mdelem_list * list,grpc_linked_mdelem * storage)202 static void unlink_storage(grpc_mdelem_list* list,
203                            grpc_linked_mdelem* storage) {
204   assert_valid_list(list);
205   if (storage->prev != nullptr) {
206     storage->prev->next = storage->next;
207   } else {
208     list->head = storage->next;
209   }
210   if (storage->next != nullptr) {
211     storage->next->prev = storage->prev;
212   } else {
213     list->tail = storage->prev;
214   }
215   list->count--;
216   assert_valid_list(list);
217 }
218 
grpc_metadata_batch_remove(grpc_metadata_batch * batch,grpc_linked_mdelem * storage)219 void grpc_metadata_batch_remove(grpc_metadata_batch* batch,
220                                 grpc_linked_mdelem* storage) {
221   assert_valid_callouts(batch);
222   maybe_unlink_callout(batch, storage);
223   unlink_storage(&batch->list, storage);
224   GRPC_MDELEM_UNREF(storage->md);
225   assert_valid_callouts(batch);
226 }
227 
grpc_metadata_batch_set_value(grpc_linked_mdelem * storage,grpc_slice value)228 void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage,
229                                    grpc_slice value) {
230   grpc_mdelem old_mdelem = storage->md;
231   grpc_mdelem new_mdelem = grpc_mdelem_from_slices(
232       grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value);
233   storage->md = new_mdelem;
234   GRPC_MDELEM_UNREF(old_mdelem);
235 }
236 
grpc_metadata_batch_substitute(grpc_metadata_batch * batch,grpc_linked_mdelem * storage,grpc_mdelem new_mdelem)237 grpc_error* grpc_metadata_batch_substitute(grpc_metadata_batch* batch,
238                                            grpc_linked_mdelem* storage,
239                                            grpc_mdelem new_mdelem) {
240   assert_valid_callouts(batch);
241   grpc_error* error = GRPC_ERROR_NONE;
242   grpc_mdelem old_mdelem = storage->md;
243   if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) {
244     maybe_unlink_callout(batch, storage);
245     storage->md = new_mdelem;
246     error = maybe_link_callout(batch, storage);
247     if (error != GRPC_ERROR_NONE) {
248       unlink_storage(&batch->list, storage);
249       GRPC_MDELEM_UNREF(storage->md);
250     }
251   } else {
252     storage->md = new_mdelem;
253   }
254   GRPC_MDELEM_UNREF(old_mdelem);
255   assert_valid_callouts(batch);
256   return error;
257 }
258 
grpc_metadata_batch_clear(grpc_metadata_batch * batch)259 void grpc_metadata_batch_clear(grpc_metadata_batch* batch) {
260   grpc_metadata_batch_destroy(batch);
261   grpc_metadata_batch_init(batch);
262 }
263 
grpc_metadata_batch_is_empty(grpc_metadata_batch * batch)264 bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch) {
265   return batch->list.head == nullptr &&
266          batch->deadline == GRPC_MILLIS_INF_FUTURE;
267 }
268 
grpc_metadata_batch_size(grpc_metadata_batch * batch)269 size_t grpc_metadata_batch_size(grpc_metadata_batch* batch) {
270   size_t size = 0;
271   for (grpc_linked_mdelem* elem = batch->list.head; elem != nullptr;
272        elem = elem->next) {
273     size += GRPC_MDELEM_LENGTH(elem->md);
274   }
275   return size;
276 }
277 
add_error(grpc_error ** composite,grpc_error * error,const char * composite_error_string)278 static void add_error(grpc_error** composite, grpc_error* error,
279                       const char* composite_error_string) {
280   if (error == GRPC_ERROR_NONE) return;
281   if (*composite == GRPC_ERROR_NONE) {
282     *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string);
283   }
284   *composite = grpc_error_add_child(*composite, error);
285 }
286 
grpc_metadata_batch_filter(grpc_metadata_batch * batch,grpc_metadata_batch_filter_func func,void * user_data,const char * composite_error_string)287 grpc_error* grpc_metadata_batch_filter(grpc_metadata_batch* batch,
288                                        grpc_metadata_batch_filter_func func,
289                                        void* user_data,
290                                        const char* composite_error_string) {
291   grpc_linked_mdelem* l = batch->list.head;
292   grpc_error* error = GRPC_ERROR_NONE;
293   while (l) {
294     grpc_linked_mdelem* next = l->next;
295     grpc_filtered_mdelem new_mdelem = func(user_data, l->md);
296     add_error(&error, new_mdelem.error, composite_error_string);
297     if (GRPC_MDISNULL(new_mdelem.md)) {
298       grpc_metadata_batch_remove(batch, l);
299     } else if (new_mdelem.md.payload != l->md.payload) {
300       grpc_metadata_batch_substitute(batch, l, new_mdelem.md);
301     }
302     l = next;
303   }
304   return error;
305 }
306 
grpc_metadata_batch_copy(grpc_metadata_batch * src,grpc_metadata_batch * dst,grpc_linked_mdelem * storage)307 void grpc_metadata_batch_copy(grpc_metadata_batch* src,
308                               grpc_metadata_batch* dst,
309                               grpc_linked_mdelem* storage) {
310   grpc_metadata_batch_init(dst);
311   dst->deadline = src->deadline;
312   size_t i = 0;
313   for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr;
314        elem = elem->next) {
315     grpc_error* error = grpc_metadata_batch_add_tail(dst, &storage[i++],
316                                                      GRPC_MDELEM_REF(elem->md));
317     // The only way that grpc_metadata_batch_add_tail() can fail is if
318     // there's a duplicate entry for a callout.  However, that can't be
319     // the case here, because we would not have been allowed to create
320     // a source batch that had that kind of conflict.
321     GPR_ASSERT(error == GRPC_ERROR_NONE);
322   }
323 }
324 
grpc_metadata_batch_move(grpc_metadata_batch * src,grpc_metadata_batch * dst)325 void grpc_metadata_batch_move(grpc_metadata_batch* src,
326                               grpc_metadata_batch* dst) {
327   *dst = *src;
328   grpc_metadata_batch_init(src);
329 }
330