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