iodine 0.7.16 → 0.7.17
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +5 -4
- data/.yardopts +8 -0
- data/CHANGELOG.md +26 -0
- data/LICENSE.txt +1 -1
- data/LIMITS.md +6 -0
- data/README.md +93 -13
- data/{SPEC-Websocket-Draft.md → SPEC-WebSocket-Draft.md} +0 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +3 -3
- data/ext/iodine/extconf.rb +21 -0
- data/ext/iodine/fio.c +659 -69
- data/ext/iodine/fio.h +350 -95
- data/ext/iodine/fio_cli.c +4 -3
- data/ext/iodine/fio_json_parser.h +1 -1
- data/ext/iodine/fio_siphash.c +13 -11
- data/ext/iodine/fio_siphash.h +6 -3
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +634 -0
- data/ext/iodine/fio_tls_openssl.c +1011 -0
- data/ext/iodine/fio_tmpfile.h +1 -1
- data/ext/iodine/fiobj.h +1 -1
- data/ext/iodine/fiobj_ary.c +1 -1
- data/ext/iodine/fiobj_ary.h +1 -1
- data/ext/iodine/fiobj_data.c +1 -1
- data/ext/iodine/fiobj_data.h +1 -1
- data/ext/iodine/fiobj_hash.c +1 -1
- data/ext/iodine/fiobj_hash.h +1 -1
- data/ext/iodine/fiobj_json.c +18 -16
- data/ext/iodine/fiobj_json.h +1 -1
- data/ext/iodine/fiobj_mustache.c +4 -0
- data/ext/iodine/fiobj_mustache.h +4 -0
- data/ext/iodine/fiobj_numbers.c +1 -1
- data/ext/iodine/fiobj_numbers.h +1 -1
- data/ext/iodine/fiobj_str.c +3 -3
- data/ext/iodine/fiobj_str.h +1 -1
- data/ext/iodine/fiobject.c +1 -1
- data/ext/iodine/fiobject.h +8 -2
- data/ext/iodine/http.c +128 -337
- data/ext/iodine/http.h +11 -18
- data/ext/iodine/http1.c +6 -6
- data/ext/iodine/http1.h +1 -1
- data/ext/iodine/http1_parser.c +1 -1
- data/ext/iodine/http1_parser.h +1 -1
- data/ext/iodine/http_internal.c +10 -8
- data/ext/iodine/http_internal.h +13 -3
- data/ext/iodine/http_mime_parser.h +1 -1
- data/ext/iodine/iodine.c +806 -22
- data/ext/iodine/iodine.h +33 -0
- data/ext/iodine/iodine_connection.c +23 -18
- data/ext/iodine/iodine_http.c +239 -225
- data/ext/iodine/iodine_http.h +4 -1
- data/ext/iodine/iodine_mustache.c +59 -54
- data/ext/iodine/iodine_pubsub.c +1 -1
- data/ext/iodine/iodine_tcp.c +34 -100
- data/ext/iodine/iodine_tcp.h +4 -0
- data/ext/iodine/iodine_tls.c +267 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1 -1
- data/ext/iodine/redis_engine.c +14 -6
- data/ext/iodine/redis_engine.h +1 -1
- data/ext/iodine/resp_parser.h +1 -1
- data/ext/iodine/websocket_parser.h +1 -1
- data/ext/iodine/websockets.c +1 -1
- data/ext/iodine/websockets.h +1 -1
- data/iodine.gemspec +2 -1
- data/lib/iodine.rb +19 -5
- data/lib/iodine/connection.rb +13 -0
- data/lib/iodine/mustache.rb +7 -24
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +1 -1
- metadata +15 -5
data/ext/iodine/fio_tmpfile.h
CHANGED
data/ext/iodine/fiobj.h
CHANGED
data/ext/iodine/fiobj_ary.c
CHANGED
data/ext/iodine/fiobj_ary.h
CHANGED
data/ext/iodine/fiobj_data.c
CHANGED
data/ext/iodine/fiobj_data.h
CHANGED
data/ext/iodine/fiobj_hash.c
CHANGED
data/ext/iodine/fiobj_hash.h
CHANGED
data/ext/iodine/fiobj_json.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz Segev, 2017-
|
2
|
+
Copyright: Boaz Segev, 2017-2019
|
3
3
|
License: MIT
|
4
4
|
*/
|
5
5
|
#include <fiobj_json.h>
|
@@ -511,7 +511,7 @@ void fiobj_test_json(void) {
|
|
511
511
|
TEST_ASSERT(o, "JSON object missing!\n");
|
512
512
|
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH),
|
513
513
|
"JSON root not a dictionary (not a hash)!\n");
|
514
|
-
FIOBJ tmp = fiobj_hash_get2(o,
|
514
|
+
FIOBJ tmp = fiobj_hash_get2(o, fiobj_hash_string("array", 5));
|
515
515
|
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY),
|
516
516
|
"JSON 'array' not an Array!\n");
|
517
517
|
TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 0)) == 1,
|
@@ -524,24 +524,26 @@ void fiobj_test_json(void) {
|
|
524
524
|
"JSON 'array' index 3 type error!\n");
|
525
525
|
TEST_ASSERT(!memcmp("boom", fiobj_obj2cstr(fiobj_ary_index(tmp, 3)).data, 4),
|
526
526
|
"JSON 'array' index 3 error!\n");
|
527
|
-
tmp = fiobj_hash_get2(o,
|
527
|
+
tmp = fiobj_hash_get2(o, fiobj_hash_string("my", 2));
|
528
528
|
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_HASH),
|
529
529
|
"JSON 'my:secret' not a Hash!\n");
|
530
|
-
TEST_ASSERT(
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
530
|
+
TEST_ASSERT(
|
531
|
+
FIOBJ_TYPE_IS(fiobj_hash_get2(tmp, fiobj_hash_string("secret", 6)),
|
532
|
+
FIOBJ_T_NUMBER),
|
533
|
+
"JSON 'my:secret' doesn't hold a number!\n");
|
534
|
+
TEST_ASSERT(
|
535
|
+
fiobj_obj2num(fiobj_hash_get2(tmp, fiobj_hash_string("secret", 6))) == 42,
|
536
|
+
"JSON 'my:secret' not 42!\n");
|
537
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_hash_string("true", 4)) == fiobj_true(),
|
537
538
|
"JSON 'true' not true!\n");
|
538
|
-
TEST_ASSERT(fiobj_hash_get2(o,
|
539
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_hash_string("false", 5)) ==
|
540
|
+
fiobj_false(),
|
539
541
|
"JSON 'false' not false!\n");
|
540
|
-
TEST_ASSERT(fiobj_hash_get2(o,
|
542
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_hash_string("null", 4)) == fiobj_null(),
|
541
543
|
"JSON 'null' not null!\n");
|
542
|
-
tmp = fiobj_hash_get2(o,
|
544
|
+
tmp = fiobj_hash_get2(o, fiobj_hash_string("float", 5));
|
543
545
|
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_FLOAT), "JSON 'float' not a float!\n");
|
544
|
-
tmp = fiobj_hash_get2(o,
|
546
|
+
tmp = fiobj_hash_get2(o, fiobj_hash_string("string", 6));
|
545
547
|
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_STRING),
|
546
548
|
"JSON 'string' not a string!\n");
|
547
549
|
TEST_ASSERT(!strcmp(fiobj_obj2cstr(tmp).data, "I \"wrote\" this."),
|
@@ -558,11 +560,11 @@ void fiobj_test_json(void) {
|
|
558
560
|
"JSON update failed to parse data.");
|
559
561
|
fiobj_free(tmp);
|
560
562
|
|
561
|
-
tmp = fiobj_hash_get2(o,
|
563
|
+
tmp = fiobj_hash_get2(o, fiobj_hash_string("array", 5));
|
562
564
|
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY),
|
563
565
|
"JSON updated 'array' not an Array!\n");
|
564
566
|
TEST_ASSERT(fiobj_ary_count(tmp) == 3, "JSON updated 'array' not updated?");
|
565
|
-
tmp = fiobj_hash_get2(o,
|
567
|
+
tmp = fiobj_hash_get2(o, fiobj_hash_string("float", 5));
|
566
568
|
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_FLOAT),
|
567
569
|
"JSON updated (old) 'float' missing!\n");
|
568
570
|
fiobj_free(o);
|
data/ext/iodine/fiobj_json.h
CHANGED
data/ext/iodine/fiobj_mustache.c
CHANGED
data/ext/iodine/fiobj_mustache.h
CHANGED
data/ext/iodine/fiobj_numbers.c
CHANGED
data/ext/iodine/fiobj_numbers.h
CHANGED
data/ext/iodine/fiobj_str.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz Segev, 2017-
|
2
|
+
Copyright: Boaz Segev, 2017-2019
|
3
3
|
License: MIT
|
4
4
|
*/
|
5
5
|
|
@@ -324,13 +324,13 @@ size_t fiobj_str_concat(FIOBJ dest, FIOBJ obj) {
|
|
324
324
|
uint64_t fiobj_str_hash(FIOBJ o) {
|
325
325
|
assert(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING));
|
326
326
|
// if (obj2str(o)->is_small) {
|
327
|
-
// return
|
327
|
+
// return fiobj_hash_string(STR_INTENAL_STR(o), STR_INTENAL_LEN(o));
|
328
328
|
// } else
|
329
329
|
if (obj2str(o)->hash) {
|
330
330
|
return obj2str(o)->hash;
|
331
331
|
}
|
332
332
|
fio_str_info_s state = fio_str_info(&obj2str(o)->str);
|
333
|
-
obj2str(o)->hash =
|
333
|
+
obj2str(o)->hash = fiobj_hash_string(state.data, state.len);
|
334
334
|
return obj2str(o)->hash;
|
335
335
|
}
|
336
336
|
|
data/ext/iodine/fiobj_str.h
CHANGED
data/ext/iodine/fiobject.c
CHANGED
data/ext/iodine/fiobject.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#ifndef H_FIOBJECT_H
|
2
2
|
/*
|
3
|
-
Copyright: Boaz Segev, 2017-
|
3
|
+
Copyright: Boaz Segev, 2017-2019
|
4
4
|
License: MIT
|
5
5
|
*/
|
6
6
|
|
@@ -27,6 +27,8 @@ types, abstracting some complexity and making dynamic type related tasks easier.
|
|
27
27
|
|
28
28
|
#include <fio_siphash.h>
|
29
29
|
|
30
|
+
#include <fio.h>
|
31
|
+
|
30
32
|
#if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
|
31
33
|
#define __attribute__(...)
|
32
34
|
#define __has_include(...) 0
|
@@ -552,7 +554,11 @@ FIO_INLINE uint64_t fiobj_obj2hash(const FIOBJ o) {
|
|
552
554
|
if (!FIOBJ_IS_ALLOCATED(o))
|
553
555
|
return (uint64_t)o;
|
554
556
|
fio_str_info_s s = fiobj_obj2cstr(o);
|
555
|
-
return
|
557
|
+
return FIO_HASH_FN(s.data, s.len, &fiobj_each2, &fiobj_free_complex_object);
|
558
|
+
}
|
559
|
+
|
560
|
+
FIO_INLINE uint64_t fiobj_hash_string(const void *data, size_t len) {
|
561
|
+
return FIO_HASH_FN(data, len, &fiobj_each2, &fiobj_free_complex_object);
|
556
562
|
}
|
557
563
|
|
558
564
|
/**
|
data/ext/iodine/http.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz Segev, 2016-
|
2
|
+
Copyright: Boaz Segev, 2016-2019
|
3
3
|
License: MIT
|
4
4
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
@@ -28,42 +28,19 @@ SSL/TLS patch
|
|
28
28
|
* The first protocol added will act as the default protocol to be selected.
|
29
29
|
*/
|
30
30
|
void __attribute__((weak))
|
31
|
-
|
32
|
-
|
31
|
+
fio_tls_alpn_add(void *tls, const char *protocol_name,
|
32
|
+
void (*callback)(intptr_t uuid, void *udata_connection,
|
33
|
+
void *udata_tls),
|
34
|
+
void *udata_tls, void (*on_cleanup)(void *udata_tls)) {
|
33
35
|
FIO_LOG_FATAL("HTTP SSL/TLS required but unavailable!");
|
34
36
|
exit(-1);
|
35
37
|
(void)tls;
|
36
38
|
(void)protocol_name;
|
37
39
|
(void)callback;
|
40
|
+
(void)on_cleanup;
|
41
|
+
(void)udata_tls;
|
38
42
|
}
|
39
|
-
#pragma weak
|
40
|
-
|
41
|
-
void __attribute__((weak))
|
42
|
-
fio_tls_accept(intptr_t uuid, void *tls, void *udata) {
|
43
|
-
FIO_LOG_FATAL("HTTP SSL/TLS required but unavailable!");
|
44
|
-
exit(-1);
|
45
|
-
(void)uuid;
|
46
|
-
(void)tls;
|
47
|
-
(void)udata;
|
48
|
-
}
|
49
|
-
#pragma weak fio_tls_accept
|
50
|
-
|
51
|
-
/**
|
52
|
-
* Establishes an SSL/TLS connection as an SSL/TLS Server, using the specified
|
53
|
-
* conetext / settings object.
|
54
|
-
*
|
55
|
-
* The `uuid` should be a socket UUID that is already connected to a peer (i.e.,
|
56
|
-
* the result of `fio_accept`).
|
57
|
-
*/
|
58
|
-
void __attribute__((weak))
|
59
|
-
fio_tls_connect(intptr_t uuid, void *tls, void *udata) {
|
60
|
-
FIO_LOG_FATAL("HTTP SSL/TLS required but unavailable!");
|
61
|
-
exit(-1);
|
62
|
-
(void)uuid;
|
63
|
-
(void)tls;
|
64
|
-
(void)udata;
|
65
|
-
}
|
66
|
-
#pragma weak fio_tls_connect
|
43
|
+
#pragma weak fio_tls_alpn_add
|
67
44
|
|
68
45
|
/* *****************************************************************************
|
69
46
|
Small Helpers
|
@@ -73,7 +50,7 @@ static inline int hex2byte(uint8_t *dest, const uint8_t *source);
|
|
73
50
|
static inline void add_content_length(http_s *r, uintptr_t length) {
|
74
51
|
static uint64_t cl_hash = 0;
|
75
52
|
if (!cl_hash)
|
76
|
-
cl_hash =
|
53
|
+
cl_hash = fiobj_hash_string("content-length", 14);
|
77
54
|
if (!fiobj_hash_get2(r->private_data.out_headers, cl_hash)) {
|
78
55
|
fiobj_hash_set(r->private_data.out_headers, HTTP_HEADER_CONTENT_LENGTH,
|
79
56
|
fiobj_num_new(length));
|
@@ -82,7 +59,7 @@ static inline void add_content_length(http_s *r, uintptr_t length) {
|
|
82
59
|
static inline void add_content_type(http_s *r) {
|
83
60
|
static uint64_t ct_hash = 0;
|
84
61
|
if (!ct_hash)
|
85
|
-
ct_hash =
|
62
|
+
ct_hash = fiobj_hash_string("content-type", 12);
|
86
63
|
if (!fiobj_hash_get2(r->private_data.out_headers, ct_hash)) {
|
87
64
|
fiobj_hash_set(r->private_data.out_headers, HTTP_HEADER_CONTENT_TYPE,
|
88
65
|
http_mimetype_find2(r->path));
|
@@ -95,10 +72,10 @@ static fio_lock_i date_lock;
|
|
95
72
|
static inline void add_date(http_s *r) {
|
96
73
|
static uint64_t date_hash = 0;
|
97
74
|
if (!date_hash)
|
98
|
-
date_hash =
|
75
|
+
date_hash = fiobj_hash_string("date", 4);
|
99
76
|
static uint64_t mod_hash = 0;
|
100
77
|
if (!mod_hash)
|
101
|
-
mod_hash =
|
78
|
+
mod_hash = fiobj_hash_string("last-modified", 13);
|
102
79
|
|
103
80
|
if (fio_last_tick().tv_sec > last_date_added) {
|
104
81
|
fio_lock(&date_lock);
|
@@ -312,13 +289,23 @@ int http_set_cookie(http_s *h, http_cookie_args_s cookie) {
|
|
312
289
|
}
|
313
290
|
} else
|
314
291
|
cookie.max_age = -1;
|
292
|
+
|
293
|
+
if (http_settings(h) && http_settings(h)->is_client) {
|
294
|
+
if (!cookie.value) {
|
295
|
+
fiobj_free(c);
|
296
|
+
return -1;
|
297
|
+
}
|
298
|
+
set_header_add(h->private_data.out_headers, HTTP_HEADER_COOKIE, c);
|
299
|
+
return 0;
|
300
|
+
}
|
301
|
+
|
315
302
|
t.data[len++] = ';';
|
316
303
|
t.data[len++] = ' ';
|
317
304
|
|
318
305
|
if (h->status_str || !h->status) { /* on first request status == 0 */
|
319
306
|
static uint64_t cookie_hash;
|
320
307
|
if (!cookie_hash)
|
321
|
-
cookie_hash =
|
308
|
+
cookie_hash = fiobj_hash_string("cookie", 6);
|
322
309
|
FIOBJ tmp = fiobj_hash_get2(h->private_data.out_headers, cookie_hash);
|
323
310
|
if (!tmp) {
|
324
311
|
set_header_add(h->private_data.out_headers, HTTP_HEADER_COOKIE, c);
|
@@ -419,10 +406,10 @@ int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len,
|
|
419
406
|
struct stat file_data = {.st_size = 0};
|
420
407
|
static uint64_t accept_enc_hash = 0;
|
421
408
|
if (!accept_enc_hash)
|
422
|
-
accept_enc_hash =
|
409
|
+
accept_enc_hash = fiobj_hash_string("accept-encoding", 15);
|
423
410
|
static uint64_t range_hash = 0;
|
424
411
|
if (!range_hash)
|
425
|
-
range_hash =
|
412
|
+
range_hash = fiobj_hash_string("range", 5);
|
426
413
|
|
427
414
|
/* create filename string */
|
428
415
|
FIOBJ filename = fiobj_str_tmp();
|
@@ -503,7 +490,7 @@ found_file:
|
|
503
490
|
/* set & test etag */
|
504
491
|
uint64_t etag = (uint64_t)file_data.st_size;
|
505
492
|
etag ^= (uint64_t)file_data.st_mtime;
|
506
|
-
etag =
|
493
|
+
etag = fiobj_hash_string(&etag, sizeof(uint64_t));
|
507
494
|
FIOBJ etag_str = fiobj_str_buf(32);
|
508
495
|
fiobj_str_resize(etag_str,
|
509
496
|
fio_base64_encode(fiobj_obj2cstr(etag_str).data,
|
@@ -514,7 +501,7 @@ found_file:
|
|
514
501
|
{
|
515
502
|
static uint64_t none_match_hash = 0;
|
516
503
|
if (!none_match_hash)
|
517
|
-
none_match_hash =
|
504
|
+
none_match_hash = fiobj_hash_string("if-none-match", 13);
|
518
505
|
FIOBJ tmp2 = fiobj_hash_get2(h->headers, none_match_hash);
|
519
506
|
if (tmp2 && fiobj_iseq(tmp2, etag_str)) {
|
520
507
|
h->status = 304;
|
@@ -528,7 +515,7 @@ found_file:
|
|
528
515
|
{
|
529
516
|
static uint64_t ifrange_hash = 0;
|
530
517
|
if (!ifrange_hash)
|
531
|
-
ifrange_hash =
|
518
|
+
ifrange_hash = fiobj_hash_string("if-range", 8);
|
532
519
|
FIOBJ tmp = fiobj_hash_get2(h->headers, ifrange_hash);
|
533
520
|
if (tmp && fiobj_iseq(tmp, etag_str)) {
|
534
521
|
fiobj_hash_delete2(h->headers, range_hash);
|
@@ -907,28 +894,28 @@ static void http_settings_free(http_settings_s *s) {
|
|
907
894
|
Listening to HTTP connections
|
908
895
|
***************************************************************************** */
|
909
896
|
|
910
|
-
static
|
911
|
-
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
912
|
-
if (!pr)
|
913
|
-
fio_close(uuid);
|
914
|
-
}
|
897
|
+
static uint8_t fio_http_at_capa = 0;
|
915
898
|
|
916
|
-
static void
|
917
|
-
|
899
|
+
static void http_on_server_protocol_http1(intptr_t uuid, void *set,
|
900
|
+
void *ignr_) {
|
918
901
|
fio_timeout_set(uuid, ((http_settings_s *)set)->timeout);
|
919
902
|
if (fio_uuid2fd(uuid) >= ((http_settings_s *)set)->max_clients) {
|
920
|
-
if (!
|
903
|
+
if (!fio_http_at_capa)
|
921
904
|
FIO_LOG_WARNING("HTTP server at capacity");
|
922
|
-
|
905
|
+
fio_http_at_capa = 1;
|
923
906
|
http_send_error2(uuid, 503, set);
|
924
907
|
fio_close(uuid);
|
925
908
|
return;
|
926
909
|
}
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
910
|
+
fio_http_at_capa = 0;
|
911
|
+
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
912
|
+
if (!pr)
|
913
|
+
fio_close(uuid);
|
914
|
+
(void)ignr_;
|
915
|
+
}
|
916
|
+
|
917
|
+
static void http_on_open(intptr_t uuid, void *set) {
|
918
|
+
http_on_server_protocol_http1(uuid, set, NULL);
|
932
919
|
}
|
933
920
|
|
934
921
|
static void http_on_finish(intptr_t uuid, void *set) {
|
@@ -961,10 +948,11 @@ intptr_t http_listen(const char *port, const char *binding,
|
|
961
948
|
http_settings_s *settings = http_settings_new(arg_settings);
|
962
949
|
settings->is_client = 0;
|
963
950
|
if (settings->tls) {
|
964
|
-
|
951
|
+
fio_tls_alpn_add(settings->tls, "http/1.1", http_on_server_protocol_http1,
|
952
|
+
NULL, NULL);
|
965
953
|
}
|
966
954
|
|
967
|
-
return fio_listen(.port = port, .address = binding,
|
955
|
+
return fio_listen(.port = port, .address = binding, .tls = arg_settings.tls,
|
968
956
|
.on_finish = http_on_finish, .on_open = http_on_open,
|
969
957
|
.udata = settings);
|
970
958
|
}
|
@@ -1008,9 +996,11 @@ static void http_on_open_client_perform(http_settings_s *set) {
|
|
1008
996
|
http_s *h = set->udata;
|
1009
997
|
set->on_response(h);
|
1010
998
|
}
|
1011
|
-
static void http_on_open_client_http1(intptr_t uuid, void *set_
|
999
|
+
static void http_on_open_client_http1(intptr_t uuid, void *set_,
|
1000
|
+
void *ignore_) {
|
1012
1001
|
http_settings_s *set = set_;
|
1013
1002
|
http_s *h = set->udata;
|
1003
|
+
fio_timeout_set(uuid, set->timeout);
|
1014
1004
|
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
1015
1005
|
if (!pr) {
|
1016
1006
|
fio_close(uuid);
|
@@ -1025,15 +1015,11 @@ static void http_on_open_client_http1(intptr_t uuid, void *set_) {
|
|
1025
1015
|
h->private_data.flag = (uintptr_t)pr;
|
1026
1016
|
h->private_data.vtbl = http1_vtable();
|
1027
1017
|
http_on_open_client_perform(set);
|
1018
|
+
(void)ignore_;
|
1028
1019
|
}
|
1029
1020
|
|
1030
1021
|
static void http_on_open_client(intptr_t uuid, void *set_) {
|
1031
|
-
|
1032
|
-
fio_timeout_set(uuid, set->timeout);
|
1033
|
-
if (set->tls)
|
1034
|
-
fio_tls_connect(uuid, set->tls, set_);
|
1035
|
-
else
|
1036
|
-
http_on_open_client_http1(uuid, set);
|
1022
|
+
http_on_open_client_http1(uuid, set_, NULL);
|
1037
1023
|
}
|
1038
1024
|
|
1039
1025
|
static void http_on_client_failed(intptr_t uuid, void *set_) {
|
@@ -1048,6 +1034,7 @@ static void http_on_client_failed(intptr_t uuid, void *set_) {
|
|
1048
1034
|
(void)uuid;
|
1049
1035
|
}
|
1050
1036
|
|
1037
|
+
intptr_t http_connect__(void); /* sublime text marker */
|
1051
1038
|
/**
|
1052
1039
|
* Connects to an HTTP server as a client.
|
1053
1040
|
*
|
@@ -1062,9 +1049,9 @@ static void http_on_client_failed(intptr_t uuid, void *set_) {
|
|
1062
1049
|
* Returns -1 on error and 0 on success. the `on_finish` callback is always
|
1063
1050
|
* called.
|
1064
1051
|
*/
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1052
|
+
intptr_t http_connect FIO_IGNORE_MACRO(const char *url,
|
1053
|
+
const char *unix_address,
|
1054
|
+
struct http_settings_s arg_settings) {
|
1068
1055
|
if (!arg_settings.on_response && !arg_settings.on_upgrade) {
|
1069
1056
|
FIO_LOG_ERROR("http_connect requires either an on_response "
|
1070
1057
|
" or an on_upgrade callback.\n");
|
@@ -1072,71 +1059,69 @@ intptr_t http_connect(const char *address,
|
|
1072
1059
|
goto on_error;
|
1073
1060
|
}
|
1074
1061
|
size_t len;
|
1075
|
-
char *a, *p;
|
1062
|
+
char *a = NULL, *p = NULL;
|
1076
1063
|
uint8_t is_websocket = 0;
|
1077
1064
|
uint8_t is_secure = 0;
|
1078
1065
|
FIOBJ path = FIOBJ_INVALID;
|
1079
|
-
if (!
|
1066
|
+
if (!url && !unix_address) {
|
1080
1067
|
FIO_LOG_ERROR("http_connect requires a valid address.");
|
1081
1068
|
errno = EINVAL;
|
1082
1069
|
goto on_error;
|
1083
1070
|
}
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
a = fio_malloc(len + 1);
|
1112
|
-
FIO_ASSERT_ALLOC(a);
|
1113
|
-
memcpy(a, address, len + 1);
|
1114
|
-
}
|
1115
|
-
p = memchr(a, '/', len);
|
1116
|
-
if (p) {
|
1117
|
-
if (len - (p - a))
|
1118
|
-
path = fiobj_str_new(p, len - (p - a));
|
1119
|
-
len = p - a;
|
1120
|
-
*p = 0;
|
1121
|
-
}
|
1122
|
-
p = memchr(a, ':', len);
|
1123
|
-
if (p) {
|
1124
|
-
len = p - a;
|
1125
|
-
*p = 0;
|
1126
|
-
if (a + len == p + 1) {
|
1127
|
-
p = NULL;
|
1071
|
+
if (url) {
|
1072
|
+
fio_url_s u = fio_url_parse(url, strlen(url));
|
1073
|
+
if (u.scheme.data &&
|
1074
|
+
(u.scheme.len == 2 || (u.scheme.len == 3 && u.scheme.data[2] == 's')) &&
|
1075
|
+
u.scheme.data[0] == 'w' && u.scheme.data[1] == 's') {
|
1076
|
+
is_websocket = 1;
|
1077
|
+
is_secure = (u.scheme.len == 3);
|
1078
|
+
} else if (u.scheme.data &&
|
1079
|
+
(u.scheme.len == 4 ||
|
1080
|
+
(u.scheme.len == 5 && u.scheme.data[4] == 's')) &&
|
1081
|
+
u.scheme.data[0] == 'h' && u.scheme.data[1] == 't' &&
|
1082
|
+
u.scheme.data[2] == 't' && u.scheme.data[3] == 'p') {
|
1083
|
+
is_secure = (u.scheme.len == 5);
|
1084
|
+
}
|
1085
|
+
if (is_secure && !arg_settings.tls) {
|
1086
|
+
FIO_LOG_ERROR("Secure connections (%.*s) require a TLS object.",
|
1087
|
+
(int)u.scheme.len, u.scheme.data);
|
1088
|
+
errno = EINVAL;
|
1089
|
+
goto on_error;
|
1090
|
+
}
|
1091
|
+
if (u.path.data) {
|
1092
|
+
path = fiobj_str_new(
|
1093
|
+
u.path.data, strlen(u.path.data)); /* copy query and target as well */
|
1094
|
+
}
|
1095
|
+
if (unix_address) {
|
1096
|
+
a = (char *)unix_address;
|
1097
|
+
len = strlen(a);
|
1128
1098
|
} else {
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1099
|
+
if (!u.host.data) {
|
1100
|
+
FIO_LOG_ERROR("http_connect requires a valid address.");
|
1101
|
+
errno = EINVAL;
|
1102
|
+
goto on_error;
|
1103
|
+
}
|
1104
|
+
/***** no more error handling, since memory is allocated *****/
|
1105
|
+
/* copy address */
|
1106
|
+
a = fio_malloc(u.host.len + 1);
|
1107
|
+
memcpy(a, u.host.data, u.host.len);
|
1108
|
+
a[u.host.len] = 0;
|
1109
|
+
len = u.host.len;
|
1110
|
+
/* copy port */
|
1111
|
+
if (u.port.data) {
|
1112
|
+
p = fio_malloc(u.port.len + 1);
|
1113
|
+
memcpy(p, u.port.data, u.port.len);
|
1114
|
+
p[u.port.len] = 0;
|
1115
|
+
} else if (is_secure) {
|
1116
|
+
p = fio_malloc(3 + 1);
|
1117
|
+
memcpy(p, "443", 3);
|
1118
|
+
p[3] = 0;
|
1119
|
+
} else {
|
1120
|
+
p = fio_malloc(2 + 1);
|
1121
|
+
memcpy(p, "80", 2);
|
1122
|
+
p[2] = 0;
|
1133
1123
|
}
|
1134
1124
|
}
|
1135
|
-
} else {
|
1136
|
-
if (is_secure)
|
1137
|
-
p = (char *)"443";
|
1138
|
-
else
|
1139
|
-
p = (char *)"80";
|
1140
1125
|
}
|
1141
1126
|
|
1142
1127
|
/* set settings */
|
@@ -1144,9 +1129,10 @@ intptr_t http_connect(const char *address,
|
|
1144
1129
|
arg_settings.timeout = 30;
|
1145
1130
|
http_settings_s *settings = http_settings_new(arg_settings);
|
1146
1131
|
settings->is_client = 1;
|
1147
|
-
if (settings->tls) {
|
1148
|
-
|
1149
|
-
|
1132
|
+
// if (settings->tls) {
|
1133
|
+
// fio_tls_alpn_add(settings->tls, "http/1.1", http_on_open_client_http1,
|
1134
|
+
// NULL, NULL);
|
1135
|
+
// }
|
1150
1136
|
|
1151
1137
|
if (!arg_settings.ws_timeout)
|
1152
1138
|
settings->ws_timeout = 0; /* allow server to dictate timeout */
|
@@ -1159,29 +1145,32 @@ intptr_t http_connect(const char *address,
|
|
1159
1145
|
h->status = 0;
|
1160
1146
|
h->path = path;
|
1161
1147
|
settings->udata = h;
|
1148
|
+
settings->tls = arg_settings.tls;
|
1162
1149
|
http_set_header2(h, (fio_str_info_s){.data = (char *)"host", .len = 4},
|
1163
1150
|
(fio_str_info_s){.data = a, .len = len});
|
1164
1151
|
intptr_t ret;
|
1165
1152
|
if (is_websocket) {
|
1166
1153
|
/* force HTTP/1.1 */
|
1167
1154
|
ret = fio_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
1168
|
-
.on_connect = http_on_open_client, .udata = settings
|
1155
|
+
.on_connect = http_on_open_client, .udata = settings,
|
1156
|
+
.tls = arg_settings.tls);
|
1169
1157
|
(void)0;
|
1170
1158
|
} else {
|
1171
1159
|
/* Allow for any HTTP version */
|
1172
1160
|
ret = fio_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
1173
|
-
.on_connect = http_on_open_client, .udata = settings
|
1161
|
+
.on_connect = http_on_open_client, .udata = settings,
|
1162
|
+
.tls = arg_settings.tls);
|
1174
1163
|
(void)0;
|
1175
1164
|
}
|
1176
|
-
|
1165
|
+
if (a != unix_address)
|
1166
|
+
fio_free(a);
|
1167
|
+
fio_free(p);
|
1177
1168
|
return ret;
|
1178
1169
|
on_error:
|
1179
1170
|
if (arg_settings.on_finish)
|
1180
1171
|
arg_settings.on_finish(&arg_settings);
|
1181
1172
|
return -1;
|
1182
1173
|
}
|
1183
|
-
#define http_connect(address, ...) \
|
1184
|
-
http_connect((address), (struct http_settings_s){__VA_ARGS__})
|
1185
1174
|
|
1186
1175
|
/* *****************************************************************************
|
1187
1176
|
HTTP Websocket Connect
|
@@ -1213,7 +1202,7 @@ static void on_websocket_http_connection_finished(http_settings_s *settings) {
|
|
1213
1202
|
int websocket_connect(const char *address, websocket_settings_s settings) {
|
1214
1203
|
websocket_settings_s *s = fio_malloc(sizeof(*s));
|
1215
1204
|
*s = settings;
|
1216
|
-
return http_connect(address, .on_request = on_websocket_http_connected,
|
1205
|
+
return http_connect(address, NULL, .on_request = on_websocket_http_connected,
|
1217
1206
|
.on_response = on_websocket_http_connected,
|
1218
1207
|
.on_finish = on_websocket_http_connection_finished,
|
1219
1208
|
.udata = s);
|
@@ -1671,7 +1660,8 @@ rebase:
|
|
1671
1660
|
/* ensure array exists and it's an array + set nested_ary */
|
1672
1661
|
const size_t len = ((cut1[-1] == ']') ? (size_t)((cut1 - 1) - name)
|
1673
1662
|
: (size_t)(cut1 - name));
|
1674
|
-
const uint64_t hash =
|
1663
|
+
const uint64_t hash =
|
1664
|
+
fiobj_hash_string(name, len); /* hash the current name */
|
1675
1665
|
nested_ary = fiobj_hash_get2(dest, hash);
|
1676
1666
|
if (!nested_ary) {
|
1677
1667
|
/* create a new nested array */
|
@@ -1708,7 +1698,8 @@ rebase:
|
|
1708
1698
|
/* we have name[key]... */
|
1709
1699
|
const size_t len = ((cut1[-1] == ']') ? (size_t)((cut1 - 1) - name)
|
1710
1700
|
: (size_t)(cut1 - name));
|
1711
|
-
const uint64_t hash =
|
1701
|
+
const uint64_t hash =
|
1702
|
+
fiobj_hash_string(name, len); /* hash the current name */
|
1712
1703
|
FIOBJ tmp = fiobj_hash_get2(dest, hash);
|
1713
1704
|
if (!tmp) {
|
1714
1705
|
/* hash doesn't exist, create it */
|
@@ -1762,7 +1753,7 @@ place_in_array:
|
|
1762
1753
|
if (name[name_len - 1] == ']')
|
1763
1754
|
--name_len;
|
1764
1755
|
{
|
1765
|
-
uint64_t hash =
|
1756
|
+
uint64_t hash = fiobj_hash_string(name, name_len);
|
1766
1757
|
FIOBJ ary = fiobj_hash_get2(dest, hash);
|
1767
1758
|
if (!ary) {
|
1768
1759
|
FIOBJ key = encoded ? http_urlstr2fiobj(name, name_len)
|
@@ -1948,7 +1939,7 @@ int http_parse_body(http_s *h) {
|
|
1948
1939
|
if (!h->body)
|
1949
1940
|
return -1;
|
1950
1941
|
if (!content_type_hash)
|
1951
|
-
content_type_hash =
|
1942
|
+
content_type_hash = fiobj_hash_string("content-type", 12);
|
1952
1943
|
FIOBJ ct = fiobj_hash_get2(h->headers, content_type_hash);
|
1953
1944
|
fio_str_info_s content_type = fiobj_obj2cstr(ct);
|
1954
1945
|
if (content_type.len < 16)
|
@@ -2500,206 +2491,6 @@ ssize_t http_decode_path_unsafe(char *dest, const char *url_data) {
|
|
2500
2491
|
*pos = 0;
|
2501
2492
|
return pos - dest;
|
2502
2493
|
}
|
2503
|
-
/* *****************************************************************************
|
2504
|
-
HTTP URL parsing
|
2505
|
-
***************************************************************************** */
|
2506
|
-
|
2507
|
-
/**
|
2508
|
-
* Parses the URI returning it's components and their lengths (no decoding
|
2509
|
-
* performed, doesn't accept decoded URIs).
|
2510
|
-
*
|
2511
|
-
* The returned string are NOT NUL terminated, they are merely locations within
|
2512
|
-
* the original string.
|
2513
|
-
*
|
2514
|
-
* This function expects any of the following formats:
|
2515
|
-
*
|
2516
|
-
* * `/complete_path?query#target`
|
2517
|
-
*
|
2518
|
-
* i.e.: /index.html?page=1#list
|
2519
|
-
*
|
2520
|
-
* * `host:port/complete_path?query#target`
|
2521
|
-
*
|
2522
|
-
* i.e.:
|
2523
|
-
* example.com/index.html
|
2524
|
-
* example.com:8080/index.html
|
2525
|
-
*
|
2526
|
-
* * `schema://user:password@host:port/path?query#target`
|
2527
|
-
*
|
2528
|
-
* i.e.: http://example.com/index.html?page=1#list
|
2529
|
-
*
|
2530
|
-
* Invalid formats might produce unexpected results. No error testing performed.
|
2531
|
-
*/
|
2532
|
-
http_url_s http_url_parse(const char *url, size_t length) {
|
2533
|
-
const char *end = url + length;
|
2534
|
-
http_url_s result = {.scheme = {.data = (char *)url, .len = 0}};
|
2535
|
-
if (length == 0) {
|
2536
|
-
return result;
|
2537
|
-
}
|
2538
|
-
if (url[0] == '/') {
|
2539
|
-
result.scheme.data = NULL;
|
2540
|
-
goto parse_path;
|
2541
|
-
}
|
2542
|
-
/* test for scheme */
|
2543
|
-
while (url < end && *url != ':' && *url != '/' && *url != '@') {
|
2544
|
-
++url;
|
2545
|
-
}
|
2546
|
-
result.scheme.len = url - result.scheme.data;
|
2547
|
-
if (url + 3 >= end || url[1] != '/' || url[2] != '/') { /* host only? */
|
2548
|
-
url = result.scheme.data;
|
2549
|
-
result.scheme = (fio_str_info_s){.data = NULL};
|
2550
|
-
goto after_scheme;
|
2551
|
-
}
|
2552
|
-
|
2553
|
-
url += 3;
|
2554
|
-
|
2555
|
-
after_scheme:
|
2556
|
-
result.user.data = (char *)url;
|
2557
|
-
while (url < end) {
|
2558
|
-
switch (*url) {
|
2559
|
-
case '/':
|
2560
|
-
/* no user name or password (or port) */
|
2561
|
-
result.host.data = result.user.data;
|
2562
|
-
result.user.data = NULL;
|
2563
|
-
result.host.len = url - result.host.data;
|
2564
|
-
goto parse_path;
|
2565
|
-
break;
|
2566
|
-
case '@':
|
2567
|
-
/* user name only, no password */
|
2568
|
-
result.user.len = url - result.user.data;
|
2569
|
-
++url;
|
2570
|
-
goto parse_host;
|
2571
|
-
break;
|
2572
|
-
case ':':
|
2573
|
-
/* user name and password (or host:port...) */
|
2574
|
-
result.user.len = url - result.user.data;
|
2575
|
-
++url;
|
2576
|
-
goto parse_password;
|
2577
|
-
break;
|
2578
|
-
default:
|
2579
|
-
++url;
|
2580
|
-
break;
|
2581
|
-
}
|
2582
|
-
}
|
2583
|
-
if (url == end) { /* possibily: http://example.com */
|
2584
|
-
result.host.data = result.user.data;
|
2585
|
-
result.user.data = NULL;
|
2586
|
-
result.host.len = url - result.host.data;
|
2587
|
-
return result;
|
2588
|
-
}
|
2589
|
-
|
2590
|
-
parse_password:
|
2591
|
-
result.password.data = (char *)url;
|
2592
|
-
while (url < end) {
|
2593
|
-
switch (*url) {
|
2594
|
-
case '/':
|
2595
|
-
/* this wasn't a user:password, but host:port */
|
2596
|
-
result.host = result.user;
|
2597
|
-
result.port = result.password;
|
2598
|
-
result.port.len = url - result.port.data;
|
2599
|
-
result.user = result.password = (fio_str_info_s){.data = NULL};
|
2600
|
-
goto parse_path;
|
2601
|
-
break;
|
2602
|
-
case '@':
|
2603
|
-
/* done */
|
2604
|
-
result.password.len = url - result.password.data;
|
2605
|
-
++url;
|
2606
|
-
goto after_pass;
|
2607
|
-
default:
|
2608
|
-
++url;
|
2609
|
-
break;
|
2610
|
-
}
|
2611
|
-
}
|
2612
|
-
after_pass:
|
2613
|
-
if (url == end) { /* possibily: http://example.com:port */
|
2614
|
-
result.host = result.user;
|
2615
|
-
result.port = result.password;
|
2616
|
-
result.port.len = url - result.port.data;
|
2617
|
-
result.user = result.password = (fio_str_info_s){.data = NULL};
|
2618
|
-
return result;
|
2619
|
-
}
|
2620
|
-
parse_host:
|
2621
|
-
/* host:port parsing */
|
2622
|
-
result.host.data = (char *)url;
|
2623
|
-
while (url < end) {
|
2624
|
-
switch (*url) {
|
2625
|
-
case '/':
|
2626
|
-
/* host only */
|
2627
|
-
result.host.len = url - result.host.data;
|
2628
|
-
goto parse_path;
|
2629
|
-
break;
|
2630
|
-
case ':':
|
2631
|
-
/* done */
|
2632
|
-
result.host.len = url - result.host.data;
|
2633
|
-
++url;
|
2634
|
-
goto after_host;
|
2635
|
-
break;
|
2636
|
-
default:
|
2637
|
-
++url;
|
2638
|
-
break;
|
2639
|
-
}
|
2640
|
-
}
|
2641
|
-
after_host:
|
2642
|
-
|
2643
|
-
if (url == end) { /* scheme://host only? */
|
2644
|
-
result.host.len = end - result.host.data;
|
2645
|
-
return result;
|
2646
|
-
}
|
2647
|
-
result.port.data = (char *)url;
|
2648
|
-
while (url < end && *url != '/') {
|
2649
|
-
++url;
|
2650
|
-
}
|
2651
|
-
result.port.len = url - result.port.data;
|
2652
|
-
|
2653
|
-
if (url == end) { /* scheme://host:port only? */
|
2654
|
-
return result;
|
2655
|
-
}
|
2656
|
-
|
2657
|
-
if (url == end) { /* scheme://host only? */
|
2658
|
-
return result;
|
2659
|
-
}
|
2660
|
-
/* path, query and target parsing */
|
2661
|
-
parse_path:
|
2662
|
-
result.path.data = (char *)url;
|
2663
|
-
while (url < end && *url != '?') {
|
2664
|
-
++url;
|
2665
|
-
}
|
2666
|
-
result.path.len = url - result.path.data;
|
2667
|
-
if (url == end) {
|
2668
|
-
return result;
|
2669
|
-
}
|
2670
|
-
++url;
|
2671
|
-
result.query.data = (char *)url;
|
2672
|
-
while (url < end && *url != '#') {
|
2673
|
-
++url;
|
2674
|
-
}
|
2675
|
-
result.query.len = url - result.query.data;
|
2676
|
-
if (url == end) {
|
2677
|
-
return result;
|
2678
|
-
}
|
2679
|
-
++url;
|
2680
|
-
result.target.data = (char *)url;
|
2681
|
-
result.target.len = end - result.target.data;
|
2682
|
-
|
2683
|
-
/* set any empty values to NULL */
|
2684
|
-
if (!result.scheme.len)
|
2685
|
-
result.scheme.data = NULL;
|
2686
|
-
if (!result.user.len)
|
2687
|
-
result.user.data = NULL;
|
2688
|
-
if (!result.password.len)
|
2689
|
-
result.password.data = NULL;
|
2690
|
-
if (!result.host.len)
|
2691
|
-
result.host.data = NULL;
|
2692
|
-
if (!result.port.len)
|
2693
|
-
result.port.data = NULL;
|
2694
|
-
if (!result.path.len)
|
2695
|
-
result.path.data = NULL;
|
2696
|
-
if (!result.query.len)
|
2697
|
-
result.query.data = NULL;
|
2698
|
-
if (!result.target.len)
|
2699
|
-
result.target.data = NULL;
|
2700
|
-
|
2701
|
-
return result;
|
2702
|
-
}
|
2703
2494
|
|
2704
2495
|
/* *****************************************************************************
|
2705
2496
|
Lookup Tables / functions
|
@@ -2719,7 +2510,7 @@ static fio_mime_set_s mime_types = FIO_SET_INIT;
|
|
2719
2510
|
/** Registers a Mime-Type to be associated with the file extension. */
|
2720
2511
|
void http_mimetype_register(char *file_ext, size_t file_ext_len,
|
2721
2512
|
FIOBJ mime_type_str) {
|
2722
|
-
uintptr_t hash =
|
2513
|
+
uintptr_t hash = fiobj_hash_string(file_ext, file_ext_len);
|
2723
2514
|
if (mime_type_str == FIOBJ_INVALID) {
|
2724
2515
|
fio_mime_set_remove(&mime_types, hash, FIOBJ_INVALID, NULL);
|
2725
2516
|
} else {
|
@@ -2739,7 +2530,7 @@ void http_mimetype_register(char *file_ext, size_t file_ext_len,
|
|
2739
2530
|
* Remember to call `fiobj_free`.
|
2740
2531
|
*/
|
2741
2532
|
FIOBJ http_mimetype_find(char *file_ext, size_t file_ext_len) {
|
2742
|
-
uintptr_t hash =
|
2533
|
+
uintptr_t hash = fiobj_hash_string(file_ext, file_ext_len);
|
2743
2534
|
return fiobj_dup(fio_mime_set_find(&mime_types, hash, FIOBJ_INVALID));
|
2744
2535
|
}
|
2745
2536
|
|