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 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 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 71 void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) { 72 assert_valid_list(&batch->list); 73 } 74 #endif /* NDEBUG */ 75 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 259 void grpc_metadata_batch_clear(grpc_metadata_batch* batch) { 260 grpc_metadata_batch_destroy(batch); 261 grpc_metadata_batch_init(batch); 262 } 263 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 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 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 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 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 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