summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/lib/http2/cache_digests.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/server/h2o/libh2o/lib/http2/cache_digests.c201
1 files changed, 201 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/lib/http2/cache_digests.c b/web/server/h2o/libh2o/lib/http2/cache_digests.c
new file mode 100644
index 00000000..fa28bd23
--- /dev/null
+++ b/web/server/h2o/libh2o/lib/http2/cache_digests.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include <limits.h>
+#include <openssl/sha.h>
+#include <stdlib.h>
+#include "golombset.h"
+#include "h2o/string_.h"
+#include "h2o/cache_digests.h"
+
+struct st_h2o_cache_digests_frame_t {
+ H2O_VECTOR(uint64_t) keys;
+ unsigned capacity_bits;
+};
+
+static void dispose_frame_vector(h2o_cache_digests_frame_vector_t *v)
+{
+ size_t i;
+ for (i = 0; i != v->size; ++i)
+ free(v->entries[i].keys.entries);
+ free(v->entries);
+}
+
+static void dispose_digests(h2o_cache_digests_t *digests)
+{
+ dispose_frame_vector(&digests->fresh.url_only);
+ dispose_frame_vector(&digests->fresh.url_and_etag);
+}
+
+void h2o_cache_digests_destroy(h2o_cache_digests_t *digests)
+{
+ dispose_digests(digests);
+ free(digests);
+}
+
+static void load_digest(h2o_cache_digests_t **digests, const char *gcs_base64, size_t gcs_base64_len, int with_validators,
+ int complete)
+{
+ h2o_cache_digests_frame_t frame = {{NULL}};
+ h2o_iovec_t gcs_bin;
+ struct st_golombset_decode_t ctx = {NULL};
+ uint64_t nbits, pbits;
+
+ /* decode base64 */
+ if ((gcs_bin = h2o_decode_base64url(NULL, gcs_base64, gcs_base64_len)).base == NULL)
+ goto Exit;
+
+ /* prepare GCS context */
+ ctx.src = (void *)(gcs_bin.base - 1);
+ ctx.src_max = (void *)(gcs_bin.base + gcs_bin.len);
+ ctx.src_shift = 0;
+
+ /* decode nbits and pbits */
+ if (golombset_decode_bits(&ctx, 5, &nbits) != 0 || golombset_decode_bits(&ctx, 5, &pbits) != 0)
+ goto Exit;
+ frame.capacity_bits = (unsigned)(nbits + pbits);
+
+ /* decode the values */
+ uint64_t value = UINT64_MAX, decoded;
+ while (golombset_decode_value(&ctx, (unsigned)pbits, &decoded) == 0) {
+ value += decoded + 1;
+ if (value >= (uint64_t)1 << frame.capacity_bits)
+ goto Exit;
+ h2o_vector_reserve(NULL, &frame.keys, frame.keys.size + 1);
+ frame.keys.entries[frame.keys.size++] = value;
+ }
+
+ /* store the result */
+ if (*digests == NULL) {
+ *digests = h2o_mem_alloc(sizeof(**digests));
+ **digests = (h2o_cache_digests_t){{{NULL}}};
+ }
+ h2o_cache_digests_frame_vector_t *target = with_validators ? &(*digests)->fresh.url_and_etag : &(*digests)->fresh.url_only;
+ h2o_vector_reserve(NULL, target, target->size + 1);
+ target->entries[target->size++] = frame;
+ frame = (h2o_cache_digests_frame_t){{NULL}};
+ (*digests)->fresh.complete = complete;
+
+Exit:
+ free(frame.keys.entries);
+ free(gcs_bin.base);
+}
+
+void h2o_cache_digests_load_header(h2o_cache_digests_t **digests, const char *value, size_t len)
+{
+ h2o_iovec_t iter = h2o_iovec_init(value, len);
+ const char *token;
+ size_t token_len;
+
+ do {
+ const char *gcs_base64;
+ size_t gcs_base64_len;
+ int reset = 0, validators = 0, complete = 0, skip = 0;
+ h2o_iovec_t token_value;
+
+ if ((gcs_base64 = h2o_next_token(&iter, ';', &gcs_base64_len, NULL)) == NULL)
+ return;
+ while ((token = h2o_next_token(&iter, ';', &token_len, &token_value)) != NULL &&
+ !h2o_memis(token, token_len, H2O_STRLIT(","))) {
+ if (h2o_lcstris(token, token_len, H2O_STRLIT("reset"))) {
+ reset = 1;
+ } else if (h2o_lcstris(token, token_len, H2O_STRLIT("validators"))) {
+ validators = 1;
+ } else if (h2o_lcstris(token, token_len, H2O_STRLIT("complete"))) {
+ complete = 1;
+ } else {
+ skip = 1;
+ }
+ }
+
+ if (reset && *digests != NULL) {
+ h2o_cache_digests_destroy(*digests);
+ *digests = NULL;
+ }
+
+ if (skip) {
+ /* not supported for the time being */
+ } else {
+ load_digest(digests, gcs_base64, gcs_base64_len, validators, complete);
+ }
+ } while (token != NULL);
+}
+
+static uint64_t calc_hash(const char *url, size_t url_len, const char *etag, size_t etag_len)
+{
+ SHA256_CTX ctx;
+ union {
+ unsigned char bytes[SHA256_DIGEST_LENGTH];
+ uint64_t u64;
+ } md;
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, url, url_len);
+ SHA256_Update(&ctx, etag, etag_len);
+ SHA256_Final(md.bytes, &ctx);
+
+ if (*(uint16_t *)"\xde\xad" == 0xdead)
+ return md.u64;
+ else
+ return __builtin_bswap64(md.u64);
+}
+
+static int cmp_key(const void *_x, const void *_y)
+{
+ uint64_t x = *(uint64_t *)_x, y = *(uint64_t *)_y;
+
+ if (x < y) {
+ return -1;
+ } else if (x > y) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int lookup(h2o_cache_digests_frame_vector_t *vector, const char *url, size_t url_len, const char *etag, size_t etag_len,
+ int is_fresh, int is_complete)
+{
+ if (vector->size != 0) {
+ uint64_t hash = calc_hash(url, url_len, etag, etag_len);
+ size_t i = 0;
+ do {
+ h2o_cache_digests_frame_t *frame = vector->entries + i;
+ uint64_t key = hash >> (64 - frame->capacity_bits);
+ if (frame->keys.entries != NULL &&
+ bsearch(&key, frame->keys.entries, frame->keys.size, sizeof(frame->keys.entries[0]), cmp_key) != NULL)
+ return is_fresh ? H2O_CACHE_DIGESTS_STATE_FRESH : H2O_CACHE_DIGESTS_STATE_STALE;
+ } while (++i != vector->size);
+ }
+
+ return is_complete ? H2O_CACHE_DIGESTS_STATE_NOT_CACHED : H2O_CACHE_DIGESTS_STATE_UNKNOWN;
+}
+
+h2o_cache_digests_state_t h2o_cache_digests_lookup_by_url(h2o_cache_digests_t *digests, const char *url, size_t url_len)
+{
+ return lookup(&digests->fresh.url_only, url, url_len, "", 0, 1, digests->fresh.complete);
+}
+
+h2o_cache_digests_state_t h2o_cache_digests_lookup_by_url_and_etag(h2o_cache_digests_t *digests, const char *url, size_t url_len,
+ const char *etag, size_t etag_len)
+{
+ return lookup(&digests->fresh.url_and_etag, url, url_len, etag, etag_len, 1, digests->fresh.complete);
+}