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