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