Coverage Report

Created: 2025-07-12 06:34

/src/h2o/lib/http2/cache_digests.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include <limits.h>
23
#include <stdlib.h>
24
#ifndef H2O_NO_OPENSSL_SUPPRESS_DEPRECATED
25
#define OPENSSL_SUPPRESS_DEPRECATED /* cache-digests is legacy and we do not want to pay the cost of switchng away from SHA256_*   \
26
                                     */
27
#endif
28
#include <openssl/sha.h>
29
#include "golombset.h"
30
#include "h2o/string_.h"
31
#include "h2o/cache_digests.h"
32
33
struct st_h2o_cache_digests_frame_t {
34
    H2O_VECTOR(uint64_t) keys;
35
    unsigned capacity_bits;
36
};
37
38
static void dispose_frame_vector(h2o_cache_digests_frame_vector_t *v)
39
326
{
40
326
    size_t i;
41
2.37k
    for (i = 0; i != v->size; ++i)
42
2.04k
        free(v->entries[i].keys.entries);
43
326
    free(v->entries);
44
326
}
45
46
static void dispose_digests(h2o_cache_digests_t *digests)
47
163
{
48
163
    dispose_frame_vector(&digests->fresh.url_only);
49
163
    dispose_frame_vector(&digests->fresh.url_and_etag);
50
163
}
51
52
void h2o_cache_digests_destroy(h2o_cache_digests_t *digests)
53
163
{
54
163
    dispose_digests(digests);
55
163
    free(digests);
56
163
}
57
58
static void load_digest(h2o_cache_digests_t **digests, const char *gcs_base64, size_t gcs_base64_len, int with_validators,
59
                        int complete)
60
12.7k
{
61
12.7k
    h2o_cache_digests_frame_t frame = {{NULL}};
62
12.7k
    h2o_iovec_t gcs_bin;
63
12.7k
    struct st_golombset_decode_t ctx = {NULL};
64
12.7k
    uint64_t nbits, pbits;
65
66
    /* decode base64 */
67
12.7k
    if ((gcs_bin = h2o_decode_base64url(NULL, gcs_base64, gcs_base64_len)).base == NULL)
68
9.97k
        goto Exit;
69
70
    /* prepare GCS context */
71
2.77k
    ctx.src = (void *)(gcs_bin.base - 1);
72
2.77k
    ctx.src_max = (void *)(gcs_bin.base + gcs_bin.len);
73
2.77k
    ctx.src_shift = 0;
74
75
    /* decode nbits and pbits */
76
2.77k
    if (golombset_decode_bits(&ctx, 5, &nbits) != 0 || golombset_decode_bits(&ctx, 5, &pbits) != 0)
77
504
        goto Exit;
78
2.27k
    frame.capacity_bits = (unsigned)(nbits + pbits);
79
80
    /* decode the values */
81
2.27k
    uint64_t value = UINT64_MAX, decoded;
82
17.1k
    while (golombset_decode_value(&ctx, (unsigned)pbits, &decoded) == 0) {
83
15.0k
        value += decoded + 1;
84
15.0k
        if (value >= (uint64_t)1 << frame.capacity_bits)
85
221
            goto Exit;
86
14.8k
        h2o_vector_reserve(NULL, &frame.keys, frame.keys.size + 1);
87
14.8k
        frame.keys.entries[frame.keys.size++] = value;
88
14.8k
    }
89
90
    /* store the result */
91
2.04k
    if (*digests == NULL) {
92
163
        *digests = h2o_mem_alloc(sizeof(**digests));
93
163
        **digests = (h2o_cache_digests_t){{{NULL}}};
94
163
    }
95
2.04k
    h2o_cache_digests_frame_vector_t *target = with_validators ? &(*digests)->fresh.url_and_etag : &(*digests)->fresh.url_only;
96
2.04k
    h2o_vector_reserve(NULL, target, target->size + 1);
97
2.04k
    target->entries[target->size++] = frame;
98
2.04k
    frame = (h2o_cache_digests_frame_t){{NULL}};
99
2.04k
    (*digests)->fresh.complete = complete;
100
101
12.7k
Exit:
102
12.7k
    free(frame.keys.entries);
103
12.7k
    free(gcs_bin.base);
104
12.7k
}
105
106
void h2o_cache_digests_load_header(h2o_cache_digests_t **digests, const char *value, size_t len)
107
5.85k
{
108
5.85k
    h2o_iovec_t iter = h2o_iovec_init(value, len);
109
5.85k
    const char *token;
110
5.85k
    size_t token_len;
111
112
16.0k
    do {
113
16.0k
        const char *gcs_base64;
114
16.0k
        size_t gcs_base64_len;
115
16.0k
        int reset = 0, validators = 0, complete = 0, skip = 0;
116
16.0k
        h2o_iovec_t token_value;
117
118
16.0k
        if ((gcs_base64 = h2o_next_token(&iter, ';', ',', &gcs_base64_len, NULL)) == NULL)
119
528
            return;
120
19.8k
        while ((token = h2o_next_token(&iter, ';', ',', &token_len, &token_value)) != NULL &&
121
19.8k
               !h2o_memis(token, token_len, H2O_STRLIT(","))) {
122
4.29k
            if (h2o_lcstris(token, token_len, H2O_STRLIT("reset"))) {
123
0
                reset = 1;
124
4.29k
            } else if (h2o_lcstris(token, token_len, H2O_STRLIT("validators"))) {
125
0
                validators = 1;
126
4.29k
            } else if (h2o_lcstris(token, token_len, H2O_STRLIT("complete"))) {
127
0
                complete = 1;
128
4.29k
            } else {
129
4.29k
                skip = 1;
130
4.29k
            }
131
4.29k
        }
132
133
15.5k
        if (reset && *digests != NULL) {
134
0
            h2o_cache_digests_destroy(*digests);
135
0
            *digests = NULL;
136
0
        }
137
138
15.5k
        if (skip) {
139
            /* not supported for the time being */
140
12.7k
        } else {
141
12.7k
            load_digest(digests, gcs_base64, gcs_base64_len, validators, complete);
142
12.7k
        }
143
15.5k
    } while (token != NULL);
144
5.85k
}
145
146
static uint64_t calc_hash(const char *url, size_t url_len, const char *etag, size_t etag_len)
147
0
{
148
0
    SHA256_CTX ctx;
149
0
    union {
150
0
        unsigned char bytes[SHA256_DIGEST_LENGTH];
151
0
        uint64_t u64;
152
0
    } md;
153
154
0
    SHA256_Init(&ctx);
155
0
    SHA256_Update(&ctx, url, url_len);
156
0
    SHA256_Update(&ctx, etag, etag_len);
157
0
    SHA256_Final(md.bytes, &ctx);
158
159
0
    if (*(uint16_t *)"\xde\xad" == 0xdead)
160
0
        return md.u64;
161
0
    else
162
0
        return __builtin_bswap64(md.u64);
163
0
}
164
165
static int cmp_key(const void *_x, const void *_y)
166
0
{
167
0
    uint64_t x = *(uint64_t *)_x, y = *(uint64_t *)_y;
168
169
0
    if (x < y) {
170
0
        return -1;
171
0
    } else if (x > y) {
172
0
        return 1;
173
0
    } else {
174
0
        return 0;
175
0
    }
176
0
}
177
178
static int lookup(h2o_cache_digests_frame_vector_t *vector, const char *url, size_t url_len, const char *etag, size_t etag_len,
179
                  int is_fresh, int is_complete)
180
0
{
181
0
    if (vector->size != 0) {
182
0
        uint64_t hash = calc_hash(url, url_len, etag, etag_len);
183
0
        size_t i = 0;
184
0
        do {
185
0
            h2o_cache_digests_frame_t *frame = vector->entries + i;
186
0
            uint64_t key = hash >> (64 - frame->capacity_bits);
187
0
            if (frame->keys.entries != NULL &&
188
0
                bsearch(&key, frame->keys.entries, frame->keys.size, sizeof(frame->keys.entries[0]), cmp_key) != NULL)
189
0
                return is_fresh ? H2O_CACHE_DIGESTS_STATE_FRESH : H2O_CACHE_DIGESTS_STATE_STALE;
190
0
        } while (++i != vector->size);
191
0
    }
192
193
0
    return is_complete ? H2O_CACHE_DIGESTS_STATE_NOT_CACHED : H2O_CACHE_DIGESTS_STATE_UNKNOWN;
194
0
}
195
196
h2o_cache_digests_state_t h2o_cache_digests_lookup_by_url(h2o_cache_digests_t *digests, const char *url, size_t url_len)
197
0
{
198
0
    return lookup(&digests->fresh.url_only, url, url_len, "", 0, 1, digests->fresh.complete);
199
0
}
200
201
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,
202
                                                                   const char *etag, size_t etag_len)
203
0
{
204
0
    return lookup(&digests->fresh.url_and_etag, url, url_len, etag, etag_len, 1, digests->fresh.complete);
205
0
}