Home | History | Annotate | Download | only in transport
      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