/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 | } |