Coverage Report

Created: 2026-03-07 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2o/lib/handler/mimemap.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2014 DeNA Co., Ltd.
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 <assert.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include "khash.h"
26
#include "h2o.h"
27
28
KHASH_MAP_INIT_STR(extmap, h2o_mimemap_type_t *)
29
30
static inline khint_t hash_mimemap_type(h2o_mimemap_type_t *mimetype)
31
101k
{
32
101k
    khint_t h = 0;
33
101k
    size_t i;
34
1.99M
    for (i = 0; i != mimetype->data.mimetype.len; ++i)
35
1.89M
        h = (h << 5) - h + (khint_t)mimetype->data.mimetype.base[i];
36
101k
    return h;
37
101k
}
38
39
static inline int mimemap_type_equals(h2o_mimemap_type_t *x, h2o_mimemap_type_t *y)
40
52.5k
{
41
52.5k
    return h2o_memis(x->data.mimetype.base, x->data.mimetype.len, y->data.mimetype.base, y->data.mimetype.len);
42
52.5k
}
43
44
KHASH_INIT(typeset, h2o_mimemap_type_t *, char, 0, hash_mimemap_type, mimemap_type_equals)
45
46
h2o_mime_attributes_t h2o_mime_attributes_as_is;
47
48
struct st_h2o_mimemap_t {
49
    khash_t(extmap) * extmap;
50
    khash_t(typeset) * typeset; /* refs point to the entries in extmap */
51
    h2o_mimemap_type_t *default_type;
52
    size_t num_dynamic;
53
};
54
55
static h2o_iovec_t dupref(const char *s)
56
816
{
57
816
    h2o_iovec_t ret;
58
816
    ret.len = strlen(s);
59
816
    ret.base = h2o_mem_alloc_shared(NULL, ret.len + 1, NULL);
60
816
    memcpy(ret.base, s, ret.len + 1);
61
816
    return ret;
62
816
}
63
64
static void on_dispose(void *_mimemap)
65
0
{
66
0
    h2o_mimemap_t *mimemap = _mimemap;
67
0
    const char *ext;
68
0
    h2o_mimemap_type_t *type;
69
70
0
    kh_destroy(typeset, mimemap->typeset);
71
0
    kh_foreach(mimemap->extmap, ext, type, {
72
0
        h2o_mem_release_shared((char *)ext);
73
0
        h2o_mem_release_shared(type);
74
0
    });
75
0
    kh_destroy(extmap, mimemap->extmap);
76
0
    h2o_mem_release_shared(mimemap->default_type);
77
0
}
78
79
static void on_unlink(h2o_mimemap_t *mimemap, h2o_mimemap_type_t *type)
80
0
{
81
0
    switch (type->type) {
82
0
    case H2O_MIMEMAP_TYPE_MIMETYPE:
83
0
        break;
84
0
    case H2O_MIMEMAP_TYPE_DYNAMIC:
85
0
        --mimemap->num_dynamic;
86
0
        break;
87
0
    }
88
0
}
89
90
static void on_link(h2o_mimemap_t *mimemap, h2o_mimemap_type_t *type)
91
822
{
92
822
    switch (type->type) {
93
822
    case H2O_MIMEMAP_TYPE_MIMETYPE:
94
822
        break;
95
0
    case H2O_MIMEMAP_TYPE_DYNAMIC:
96
0
        ++mimemap->num_dynamic;
97
0
        break;
98
822
    }
99
822
}
100
101
static void rebuild_typeset(h2o_mimemap_t *mimemap)
102
822
{
103
822
    kh_clear(typeset, mimemap->typeset);
104
105
822
    const char *ext;
106
822
    h2o_mimemap_type_t *mime;
107
822
    kh_foreach(mimemap->extmap, ext, mime, {
108
822
        if (mime->type == H2O_MIMEMAP_TYPE_MIMETYPE) {
109
822
            khiter_t iter = kh_get(typeset, mimemap->typeset, mime);
110
822
            if (iter == kh_end(mimemap->typeset)) {
111
822
                int r;
112
822
                kh_put(typeset, mimemap->typeset, mime, &r);
113
822
            }
114
822
        }
115
822
    });
116
822
}
117
118
static h2o_mimemap_type_t *create_extension_type(const char *mime, h2o_mime_attributes_t *attr)
119
618
{
120
618
    h2o_mimemap_type_t *type = h2o_mem_alloc_shared(NULL, sizeof(*type) + strlen(mime) + 1, NULL);
121
618
    size_t i;
122
123
618
    memset(type, 0, sizeof(*type));
124
125
618
    type->type = H2O_MIMEMAP_TYPE_MIMETYPE;
126
127
    /* normalize-copy type->data.mimetype */
128
618
    type->data.mimetype.base = (char *)type + sizeof(*type);
129
12.0k
    for (i = 0; mime[i] != '\0' && mime[i] != ';'; ++i)
130
11.4k
        type->data.mimetype.base[i] = h2o_tolower(mime[i]);
131
618
    for (; mime[i] != '\0'; ++i)
132
0
        type->data.mimetype.base[i] = mime[i];
133
618
    type->data.mimetype.base[i] = '\0';
134
618
    type->data.mimetype.len = i;
135
136
618
    if (attr != NULL) {
137
0
        type->data.attr = *attr;
138
618
    } else {
139
618
        h2o_mimemap_get_default_attributes(mime, &type->data.attr);
140
618
    }
141
142
618
    return type;
143
618
}
144
145
static void dispose_dynamic_type(h2o_mimemap_type_t *type)
146
0
{
147
0
    h2o_config_dispose_pathconf(&type->data.dynamic.pathconf);
148
0
}
149
150
static h2o_mimemap_type_t *create_dynamic_type(h2o_globalconf_t *globalconf, h2o_mimemap_t *mimemap)
151
0
{
152
0
    h2o_mimemap_type_t *type = h2o_mem_alloc_shared(NULL, sizeof(*type), (void (*)(void *))dispose_dynamic_type);
153
154
0
    type->type = H2O_MIMEMAP_TYPE_DYNAMIC;
155
0
    memset(&type->data.dynamic, 0, sizeof(type->data.dynamic));
156
0
    h2o_config_init_pathconf(&type->data.dynamic.pathconf, globalconf, NULL, mimemap);
157
158
0
    return type;
159
0
}
160
161
h2o_mimemap_t *h2o_mimemap_create()
162
6
{
163
6
    h2o_mimemap_t *mimemap = h2o_mem_alloc_shared(NULL, sizeof(*mimemap), on_dispose);
164
165
6
    mimemap->extmap = kh_init(extmap);
166
6
    mimemap->typeset = kh_init(typeset);
167
6
    mimemap->default_type = create_extension_type("application/octet-stream", NULL);
168
6
    mimemap->num_dynamic = 0;
169
6
    on_link(mimemap, mimemap->default_type);
170
171
6
    { /* setup the tiny default */
172
6
        static const char *default_types[] = {
173
816
#define MIMEMAP(ext, mime) ext, mime,
174
6
#include "mimemap/defaults.c.h"
175
6
#undef MIMEMAP
176
6
            NULL};
177
6
        const char **p;
178
822
        for (p = default_types; *p != NULL; p += 2)
179
816
            h2o_mimemap_define_mimetype(mimemap, p[0], p[1], NULL);
180
6
    }
181
6
    rebuild_typeset(mimemap);
182
183
6
    return mimemap;
184
6
}
185
186
h2o_mimemap_t *h2o_mimemap_clone(h2o_mimemap_t *src)
187
0
{
188
0
    h2o_mimemap_t *dst = h2o_mem_alloc_shared(NULL, sizeof(*dst), on_dispose);
189
0
    const char *ext;
190
0
    h2o_mimemap_type_t *type;
191
192
0
    dst->extmap = kh_init(extmap);
193
0
    dst->typeset = kh_init(typeset);
194
0
    kh_foreach(src->extmap, ext, type, {
195
0
        int r;
196
0
        khiter_t iter = kh_put(extmap, dst->extmap, ext, &r);
197
0
        kh_val(dst->extmap, iter) = type;
198
0
        h2o_mem_addref_shared((char *)ext);
199
0
        h2o_mem_addref_shared(type);
200
0
        on_link(dst, type);
201
0
    });
202
0
    dst->default_type = src->default_type;
203
0
    h2o_mem_addref_shared(dst->default_type);
204
0
    on_link(dst, dst->default_type);
205
0
    rebuild_typeset(dst);
206
207
0
    return dst;
208
0
}
209
210
#define FOREACH_TYPE(mimemap, block)                                                                                               \
211
3
    do {                                                                                                                           \
212
3
        const char *ext;                                                                                                           \
213
3
        h2o_mimemap_type_t *type;                                                                                                  \
214
3
        type = mimemap->default_type;                                                                                              \
215
3
        {block};                                                                                                                   \
216
3
        kh_foreach(mimemap->extmap, ext, type, {block});                                                                           \
217
3
    } while (0)
218
219
void h2o_mimemap_on_context_init(h2o_mimemap_t *mimemap, h2o_context_t *ctx)
220
3
{
221
3
    FOREACH_TYPE(mimemap, {
222
3
        switch (type->type) {
223
3
        case H2O_MIMEMAP_TYPE_DYNAMIC:
224
3
            h2o_context_init_pathconf_context(ctx, &type->data.dynamic.pathconf);
225
3
            break;
226
3
        case H2O_MIMEMAP_TYPE_MIMETYPE:
227
3
            break;
228
3
        }
229
3
    });
230
3
}
231
232
void h2o_mimemap_on_context_dispose(h2o_mimemap_t *mimemap, h2o_context_t *ctx)
233
0
{
234
0
    FOREACH_TYPE(mimemap, {
235
0
        switch (type->type) {
236
0
        case H2O_MIMEMAP_TYPE_DYNAMIC:
237
0
            h2o_context_dispose_pathconf_context(ctx, &type->data.dynamic.pathconf);
238
0
            break;
239
0
        case H2O_MIMEMAP_TYPE_MIMETYPE:
240
0
            break;
241
0
        }
242
0
    });
243
0
}
244
245
#undef FOREACH_TYPE
246
247
int h2o_mimemap_has_dynamic_type(h2o_mimemap_t *mimemap)
248
10.1k
{
249
10.1k
    return mimemap->num_dynamic != 0;
250
10.1k
}
251
252
void set_default_type(h2o_mimemap_t *mimemap, h2o_mimemap_type_t *type)
253
0
{
254
    /* unlink the old one */
255
0
    on_unlink(mimemap, mimemap->default_type);
256
0
    h2o_mem_release_shared(mimemap->default_type);
257
258
    /* update */
259
0
    h2o_mem_addref_shared(type);
260
0
    mimemap->default_type = type;
261
0
    on_link(mimemap, type);
262
0
    rebuild_typeset(mimemap);
263
0
}
264
265
void h2o_mimemap_set_default_type(h2o_mimemap_t *mimemap, const char *mime, h2o_mime_attributes_t *attr)
266
0
{
267
0
    h2o_mimemap_type_t *new_type;
268
269
    /* obtain or create new type */
270
0
    if ((new_type = h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(mime, strlen(mime)), 1)) != NULL &&
271
0
        (attr == NULL || memcmp(&new_type->data.attr, attr, sizeof(*attr)) == 0)) {
272
0
        h2o_mem_addref_shared(new_type);
273
0
    } else {
274
0
        new_type = create_extension_type(mime, attr);
275
0
    }
276
277
0
    set_default_type(mimemap, new_type);
278
0
    h2o_mem_release_shared(new_type);
279
0
}
280
281
static void set_type(h2o_mimemap_t *mimemap, const char *ext, h2o_mimemap_type_t *type)
282
816
{
283
    /* obtain key, and remove the old value */
284
816
    khiter_t iter = kh_get(extmap, mimemap->extmap, ext);
285
816
    if (iter != kh_end(mimemap->extmap)) {
286
0
        h2o_mimemap_type_t *oldtype = kh_val(mimemap->extmap, iter);
287
0
        on_unlink(mimemap, oldtype);
288
0
        h2o_mem_release_shared(oldtype);
289
816
    } else {
290
816
        int ret;
291
816
        iter = kh_put(extmap, mimemap->extmap, dupref(ext).base, &ret);
292
816
        assert(iter != kh_end(mimemap->extmap));
293
816
    }
294
295
    /* update */
296
816
    h2o_mem_addref_shared(type);
297
816
    kh_val(mimemap->extmap, iter) = type;
298
816
    on_link(mimemap, type);
299
816
    rebuild_typeset(mimemap);
300
816
}
301
302
void h2o_mimemap_define_mimetype(h2o_mimemap_t *mimemap, const char *ext, const char *mime, h2o_mime_attributes_t *attr)
303
816
{
304
816
    h2o_mimemap_type_t *new_type;
305
306
816
    if ((new_type = h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(mime, strlen(mime)), 1)) != NULL &&
307
204
        (attr == NULL || memcmp(&new_type->data.attr, attr, sizeof(*attr)) == 0)) {
308
204
        h2o_mem_addref_shared(new_type);
309
612
    } else {
310
612
        new_type = create_extension_type(mime, attr);
311
612
    }
312
816
    set_type(mimemap, ext, new_type);
313
816
    h2o_mem_release_shared(new_type);
314
816
}
315
316
h2o_mimemap_type_t *h2o_mimemap_define_dynamic(h2o_mimemap_t *mimemap, const char **exts, h2o_globalconf_t *globalconf)
317
0
{
318
    /* FIXME: fix memory leak introduced by this a cyclic link (mimemap -> new_type -> mimemap)
319
     * note also that we may want to update the reference from the dynamic type to the mimemap as we clone the mimemap,
320
     * but doing so naively would cause unnecessary copies of fastcgi.spawns... */
321
0
    h2o_mimemap_type_t *new_type = create_dynamic_type(globalconf, mimemap);
322
0
    size_t i;
323
324
0
    for (i = 0; exts[i] != NULL; ++i) {
325
0
        if (exts[i][0] == '\0') {
326
            /* empty string means default */
327
0
            set_default_type(mimemap, new_type);
328
0
        } else {
329
0
            set_type(mimemap, exts[i], new_type);
330
0
        }
331
0
    }
332
0
    h2o_mem_release_shared(new_type);
333
0
    return new_type;
334
0
}
335
336
void h2o_mimemap_remove_type(h2o_mimemap_t *mimemap, const char *ext)
337
0
{
338
0
    khiter_t iter = kh_get(extmap, mimemap->extmap, ext);
339
0
    if (iter != kh_end(mimemap->extmap)) {
340
0
        const char *key = kh_key(mimemap->extmap, iter);
341
0
        h2o_mimemap_type_t *type = kh_val(mimemap->extmap, iter);
342
0
        on_unlink(mimemap, type);
343
0
        h2o_mem_release_shared(type);
344
0
        kh_del(extmap, mimemap->extmap, iter);
345
0
        h2o_mem_release_shared((char *)key);
346
0
        rebuild_typeset(mimemap);
347
0
    }
348
0
}
349
350
void h2o_mimemap_clear_types(h2o_mimemap_t *mimemap)
351
0
{
352
0
    khiter_t iter;
353
354
0
    for (iter = kh_begin(mimemap->extmap); iter != kh_end(mimemap->extmap); ++iter) {
355
0
        if (!kh_exist(mimemap->extmap, iter))
356
0
            continue;
357
0
        const char *key = kh_key(mimemap->extmap, iter);
358
0
        h2o_mimemap_type_t *type = kh_val(mimemap->extmap, iter);
359
0
        on_unlink(mimemap, type);
360
0
        h2o_mem_release_shared(type);
361
0
        kh_del(extmap, mimemap->extmap, iter);
362
0
        h2o_mem_release_shared((char *)key);
363
0
    }
364
0
    rebuild_typeset(mimemap);
365
0
}
366
367
h2o_mimemap_type_t *h2o_mimemap_get_default_type(h2o_mimemap_t *mimemap)
368
0
{
369
0
    return mimemap->default_type;
370
0
}
371
372
h2o_mimemap_type_t *h2o_mimemap_get_type_by_extension(h2o_mimemap_t *mimemap, h2o_iovec_t ext)
373
0
{
374
0
    char lcbuf[256];
375
376
0
    if (0 < ext.len && ext.len < sizeof(lcbuf)) {
377
0
        memcpy(lcbuf, ext.base, ext.len);
378
0
        h2o_strtolower(lcbuf, ext.len);
379
0
        lcbuf[ext.len] = '\0';
380
0
        khiter_t iter = kh_get(extmap, mimemap->extmap, lcbuf);
381
0
        if (iter != kh_end(mimemap->extmap))
382
0
            return kh_val(mimemap->extmap, iter);
383
0
    }
384
0
    return mimemap->default_type;
385
0
}
386
387
h2o_mimemap_type_t *h2o_mimemap_get_type_by_mimetype(h2o_mimemap_t *mimemap, h2o_iovec_t mime, int exact_match_only)
388
816
{
389
816
    h2o_mimemap_type_t key = {H2O_MIMEMAP_TYPE_MIMETYPE};
390
816
    khiter_t iter;
391
816
    size_t type_end_at;
392
393
    /* exact match */
394
816
    key.data.mimetype = mime;
395
816
    if ((iter = kh_get(typeset, mimemap->typeset, &key)) != kh_end(mimemap->typeset))
396
204
        return kh_key(mimemap->typeset, iter);
397
398
612
    if (!exact_match_only) {
399
        /* determine the end of the type */
400
0
        for (type_end_at = 0; type_end_at != mime.len; ++type_end_at)
401
0
            if (mime.base[type_end_at] == ';' || mime.base[type_end_at] == ' ')
402
0
                goto HasAttributes;
403
0
    }
404
612
    return NULL;
405
406
0
HasAttributes:
407
    /* perform search without attributes */
408
0
    key.data.mimetype.len = type_end_at;
409
0
    if ((iter = kh_get(typeset, mimemap->typeset, &key)) != kh_end(mimemap->typeset))
410
0
        return kh_key(mimemap->typeset, iter);
411
412
0
    return NULL;
413
0
}
414
415
void h2o_mimemap_get_default_attributes(const char *mime, h2o_mime_attributes_t *attr)
416
618
{
417
618
    size_t mime_len;
418
419
12.0k
    for (mime_len = 0; !(mime[mime_len] == '\0' || mime[mime_len] == ';'); ++mime_len)
420
11.4k
        ;
421
422
618
    *attr = (h2o_mime_attributes_t){0};
423
424
7.95k
#define MIME_IS(x) h2o_memis(mime, mime_len, H2O_STRLIT(x))
425
1.20k
#define MIME_STARTS_WITH(x) (mime_len >= sizeof(x) - 1 && memcmp(mime, x, sizeof(x) - 1) == 0)
426
1.66k
#define MIME_ENDS_WITH(x) (mime_len >= sizeof(x) - 1 && memcmp(mime + mime_len - (sizeof(x) - 1), x, sizeof(x) - 1) == 0)
427
428
618
    if (MIME_IS("text/css") || MIME_IS("application/ecmascript") || MIME_IS("application/javascript") ||
429
612
        MIME_IS("text/ecmascript") || MIME_IS("text/javascript")) {
430
12
        attr->is_compressible = 1;
431
12
        attr->priority = H2O_MIME_ATTRIBUTE_PRIORITY_HIGHEST;
432
606
    } else if (MIME_IS("application/json") || MIME_IS("application/xml") || MIME_STARTS_WITH("text/") || MIME_ENDS_WITH("+json") ||
433
522
               MIME_ENDS_WITH("+xml")) {
434
132
        attr->is_compressible = 1;
435
132
    }
436
437
618
#undef MIME_IS
438
618
#undef MIME_STARTS_WITH
439
618
#undef MIME_ENDS_WITH
440
618
}