blob: 25cc65d5f1957c60bb85593f9dde01d285f4945b [file] [log] [blame]
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/lib/transport/metadata_batch.h"
#include <stdbool.h>
#include <string.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/slice/slice_string_helpers.h"
static void assert_valid_list(grpc_mdelem_list *list) {
#ifndef NDEBUG
grpc_linked_mdelem *l;
GPR_ASSERT((list->head == NULL) == (list->tail == NULL));
if (!list->head) return;
GPR_ASSERT(list->head->prev == NULL);
GPR_ASSERT(list->tail->next == NULL);
GPR_ASSERT((list->head == list->tail) == (list->head->next == NULL));
size_t verified_count = 0;
for (l = list->head; l; l = l->next) {
GPR_ASSERT(!GRPC_MDISNULL(l->md));
GPR_ASSERT((l->prev == NULL) == (l == list->head));
GPR_ASSERT((l->next == NULL) == (l == list->tail));
if (l->next) GPR_ASSERT(l->next->prev == l);
if (l->prev) GPR_ASSERT(l->prev->next == l);
verified_count++;
}
GPR_ASSERT(list->count == verified_count);
#endif /* NDEBUG */
}
static void assert_valid_callouts(grpc_metadata_batch *batch) {
#ifndef NDEBUG
for (grpc_linked_mdelem *l = batch->list.head; l != NULL; l = l->next) {
grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md));
grpc_metadata_batch_callouts_index callout_idx =
GRPC_BATCH_INDEX_OF(key_interned);
if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) {
GPR_ASSERT(batch->idx.array[callout_idx] == l);
}
grpc_slice_unref(key_interned);
}
#endif
}
#ifndef NDEBUG
void grpc_metadata_batch_assert_ok(grpc_metadata_batch *batch) {
assert_valid_list(&batch->list);
}
#endif /* NDEBUG */
void grpc_metadata_batch_init(grpc_metadata_batch *batch) {
memset(batch, 0, sizeof(*batch));
batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
}
void grpc_metadata_batch_destroy(grpc_exec_ctx *exec_ctx,
grpc_metadata_batch *batch) {
grpc_linked_mdelem *l;
for (l = batch->list.head; l; l = l->next) {
GRPC_MDELEM_UNREF(exec_ctx, l->md);
}
}
grpc_error *grpc_attach_md_to_error(grpc_error *src, grpc_mdelem md) {
char *k = grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_ASCII);
char *v = grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_ASCII);
grpc_error *out = grpc_error_set_str(
grpc_error_set_str(src, GRPC_ERROR_STR_KEY, k), GRPC_ERROR_STR_VALUE, v);
gpr_free(k);
gpr_free(v);
return out;
}
static grpc_error *maybe_link_callout(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage)
GRPC_MUST_USE_RESULT;
static grpc_error *maybe_link_callout(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage) {
grpc_metadata_batch_callouts_index idx =
GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
return GRPC_ERROR_NONE;
}
if (batch->idx.array[idx] == NULL) {
batch->idx.array[idx] = storage;
return GRPC_ERROR_NONE;
}
return grpc_attach_md_to_error(
GRPC_ERROR_CREATE("Unallowed duplicate metadata"), storage->md);
}
static void maybe_unlink_callout(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage) {
grpc_metadata_batch_callouts_index idx =
GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md));
if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
return;
}
GPR_ASSERT(batch->idx.array[idx] != NULL);
batch->idx.array[idx] = NULL;
}
grpc_error *grpc_metadata_batch_add_head(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage,
grpc_mdelem elem_to_add) {
GPR_ASSERT(!GRPC_MDISNULL(elem_to_add));
storage->md = elem_to_add;
return grpc_metadata_batch_link_head(batch, storage);
}
static void link_head(grpc_mdelem_list *list, grpc_linked_mdelem *storage) {
assert_valid_list(list);
GPR_ASSERT(!GRPC_MDISNULL(storage->md));
storage->prev = NULL;
storage->next = list->head;
if (list->head != NULL) {
list->head->prev = storage;
} else {
list->tail = storage;
}
list->head = storage;
list->count++;
assert_valid_list(list);
}
grpc_error *grpc_metadata_batch_link_head(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage) {
assert_valid_callouts(batch);
grpc_error *err = maybe_link_callout(batch, storage);
if (err != GRPC_ERROR_NONE) {
assert_valid_callouts(batch);
return err;
}
link_head(&batch->list, storage);
assert_valid_callouts(batch);
return GRPC_ERROR_NONE;
}
grpc_error *grpc_metadata_batch_add_tail(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage,
grpc_mdelem elem_to_add) {
GPR_ASSERT(!GRPC_MDISNULL(elem_to_add));
storage->md = elem_to_add;
return grpc_metadata_batch_link_tail(batch, storage);
}
static void link_tail(grpc_mdelem_list *list, grpc_linked_mdelem *storage) {
assert_valid_list(list);
GPR_ASSERT(!GRPC_MDISNULL(storage->md));
storage->prev = list->tail;
storage->next = NULL;
storage->reserved = NULL;
if (list->tail != NULL) {
list->tail->next = storage;
} else {
list->head = storage;
}
list->tail = storage;
list->count++;
assert_valid_list(list);
}
grpc_error *grpc_metadata_batch_link_tail(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage) {
assert_valid_callouts(batch);
grpc_error *err = maybe_link_callout(batch, storage);
if (err != GRPC_ERROR_NONE) {
assert_valid_callouts(batch);
return err;
}
link_tail(&batch->list, storage);
assert_valid_callouts(batch);
return GRPC_ERROR_NONE;
}
static void unlink_storage(grpc_mdelem_list *list,
grpc_linked_mdelem *storage) {
assert_valid_list(list);
if (storage->prev != NULL) {
storage->prev->next = storage->next;
} else {
list->head = storage->next;
}
if (storage->next != NULL) {
storage->next->prev = storage->prev;
} else {
list->tail = storage->prev;
}
list->count--;
assert_valid_list(list);
}
void grpc_metadata_batch_remove(grpc_metadata_batch *batch,
grpc_linked_mdelem *storage) {
assert_valid_callouts(batch);
maybe_unlink_callout(batch, storage);
unlink_storage(&batch->list, storage);
assert_valid_callouts(batch);
}
void grpc_metadata_batch_set_value(grpc_exec_ctx *exec_ctx,
grpc_linked_mdelem *storage,
grpc_slice value) {
grpc_mdelem old = storage->md;
grpc_mdelem new =
grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref(GRPC_MDKEY(old)), value);
storage->md = new;
GRPC_MDELEM_UNREF(exec_ctx, old);
}
grpc_error *grpc_metadata_batch_substitute(grpc_exec_ctx *exec_ctx,
grpc_metadata_batch *batch,
grpc_linked_mdelem *storage,
grpc_mdelem new) {
grpc_error *error = GRPC_ERROR_NONE;
grpc_mdelem old = storage->md;
if (!grpc_slice_eq(GRPC_MDKEY(new), GRPC_MDKEY(old))) {
maybe_unlink_callout(batch, storage);
storage->md = new;
error = maybe_link_callout(batch, storage);
} else {
storage->md = new;
}
GRPC_MDELEM_UNREF(exec_ctx, old);
return error;
}
void grpc_metadata_batch_clear(grpc_exec_ctx *exec_ctx,
grpc_metadata_batch *batch) {
grpc_metadata_batch_destroy(exec_ctx, batch);
grpc_metadata_batch_init(batch);
}
bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) {
return batch->list.head == NULL &&
gpr_time_cmp(gpr_inf_future(batch->deadline.clock_type),
batch->deadline) == 0;
}
size_t grpc_metadata_batch_size(grpc_metadata_batch *batch) {
size_t size = 0;
for (grpc_linked_mdelem *elem = batch->list.head; elem != NULL;
elem = elem->next) {
size += GRPC_MDELEM_LENGTH(elem->md);
}
return size;
}