iodine 0.6.5 → 0.7.0
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/CHANGELOG.md +11 -0
- data/README.md +4 -4
- data/SPEC-Websocket-Draft.md +3 -6
- data/bin/mustache.rb +128 -0
- data/examples/test_template.mustache +16 -0
- data/ext/iodine/fio.c +9397 -0
- data/ext/iodine/fio.h +4723 -0
- data/ext/iodine/fio_ary.h +353 -54
- data/ext/iodine/fio_cli.c +351 -361
- data/ext/iodine/fio_cli.h +84 -105
- data/ext/iodine/fio_hashmap.h +70 -16
- data/ext/iodine/fio_json_parser.h +35 -24
- data/ext/iodine/fio_siphash.c +104 -4
- data/ext/iodine/fio_siphash.h +18 -2
- data/ext/iodine/fio_str.h +1218 -0
- data/ext/iodine/fio_tmpfile.h +1 -1
- data/ext/iodine/fiobj.h +13 -8
- data/ext/iodine/fiobj4sock.h +6 -8
- data/ext/iodine/fiobj_ary.c +107 -17
- data/ext/iodine/fiobj_ary.h +36 -4
- data/ext/iodine/fiobj_data.c +146 -127
- data/ext/iodine/fiobj_data.h +25 -23
- data/ext/iodine/fiobj_hash.c +7 -7
- data/ext/iodine/fiobj_hash.h +6 -5
- data/ext/iodine/fiobj_json.c +20 -17
- data/ext/iodine/fiobj_json.h +5 -5
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +310 -0
- data/ext/iodine/fiobj_mustache.h +40 -0
- data/ext/iodine/fiobj_numbers.c +199 -94
- data/ext/iodine/fiobj_numbers.h +7 -7
- data/ext/iodine/fiobj_str.c +142 -333
- data/ext/iodine/fiobj_str.h +65 -55
- data/ext/iodine/fiobject.c +49 -11
- data/ext/iodine/fiobject.h +40 -39
- data/ext/iodine/http.c +382 -190
- data/ext/iodine/http.h +124 -80
- data/ext/iodine/http1.c +99 -127
- data/ext/iodine/http1.h +5 -5
- data/ext/iodine/http1_parser.c +3 -2
- data/ext/iodine/http1_parser.h +2 -2
- data/ext/iodine/http_internal.c +14 -12
- data/ext/iodine/http_internal.h +25 -19
- data/ext/iodine/iodine.c +37 -18
- data/ext/iodine/iodine.h +4 -0
- data/ext/iodine/iodine_caller.c +9 -2
- data/ext/iodine/iodine_caller.h +2 -0
- data/ext/iodine/iodine_connection.c +82 -117
- data/ext/iodine/iodine_defer.c +57 -50
- data/ext/iodine/iodine_defer.h +0 -1
- data/ext/iodine/iodine_fiobj2rb.h +4 -2
- data/ext/iodine/iodine_helpers.c +4 -4
- data/ext/iodine/iodine_http.c +25 -32
- data/ext/iodine/iodine_json.c +2 -1
- data/ext/iodine/iodine_mustache.c +423 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +48 -153
- data/ext/iodine/iodine_pubsub.h +5 -4
- data/ext/iodine/iodine_rack_io.c +7 -5
- data/ext/iodine/iodine_store.c +16 -13
- data/ext/iodine/iodine_tcp.c +26 -34
- data/ext/iodine/mustache_parser.h +1085 -0
- data/ext/iodine/redis_engine.c +740 -646
- data/ext/iodine/redis_engine.h +13 -15
- data/ext/iodine/resp_parser.h +11 -5
- data/ext/iodine/websocket_parser.h +13 -13
- data/ext/iodine/websockets.c +240 -393
- data/ext/iodine/websockets.h +52 -113
- data/lib/iodine.rb +1 -1
- data/lib/iodine/mustache.rb +140 -0
- data/lib/iodine/version.rb +1 -1
- metadata +15 -28
- data/ext/iodine/defer.c +0 -566
- data/ext/iodine/defer.h +0 -148
- data/ext/iodine/evio.c +0 -26
- data/ext/iodine/evio.h +0 -161
- data/ext/iodine/evio_callbacks.c +0 -26
- data/ext/iodine/evio_epoll.c +0 -251
- data/ext/iodine/evio_kqueue.c +0 -194
- data/ext/iodine/facil.c +0 -2325
- data/ext/iodine/facil.h +0 -616
- data/ext/iodine/fio_base64.c +0 -277
- data/ext/iodine/fio_base64.h +0 -71
- data/ext/iodine/fio_llist.h +0 -257
- data/ext/iodine/fio_mem.c +0 -675
- data/ext/iodine/fio_mem.h +0 -143
- data/ext/iodine/fio_random.c +0 -248
- data/ext/iodine/fio_random.h +0 -45
- data/ext/iodine/fio_sha1.c +0 -362
- data/ext/iodine/fio_sha1.h +0 -107
- data/ext/iodine/fio_sha2.c +0 -842
- data/ext/iodine/fio_sha2.h +0 -169
- data/ext/iodine/pubsub.c +0 -867
- data/ext/iodine/pubsub.h +0 -221
- data/ext/iodine/sock.c +0 -1366
- data/ext/iodine/sock.h +0 -566
- data/ext/iodine/spnlock.inc +0 -111
data/ext/iodine/http.c
CHANGED
@@ -5,11 +5,10 @@ License: MIT
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
7
|
|
8
|
-
#include
|
8
|
+
#include <fio.h>
|
9
9
|
|
10
|
-
#include
|
11
|
-
#include
|
12
|
-
#include "http_internal.h"
|
10
|
+
#include <http1.h>
|
11
|
+
#include <http_internal.h>
|
13
12
|
|
14
13
|
#include <ctype.h>
|
15
14
|
#include <fcntl.h>
|
@@ -19,8 +18,6 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
19
18
|
#include <sys/types.h>
|
20
19
|
#include <unistd.h>
|
21
20
|
|
22
|
-
#include "fio_mem.h"
|
23
|
-
|
24
21
|
/* *****************************************************************************
|
25
22
|
Patch for OSX version < 10.12 from https://stackoverflow.com/a/9781275/4025095
|
26
23
|
***************************************************************************** */
|
@@ -68,7 +65,7 @@ static inline void add_content_type(http_s *r) {
|
|
68
65
|
|
69
66
|
static FIOBJ current_date;
|
70
67
|
static time_t last_date_added;
|
71
|
-
static
|
68
|
+
static fio_lock_i date_lock;
|
72
69
|
static inline void add_date(http_s *r) {
|
73
70
|
static uint64_t date_hash = 0;
|
74
71
|
if (!date_hash)
|
@@ -77,18 +74,18 @@ static inline void add_date(http_s *r) {
|
|
77
74
|
if (!mod_hash)
|
78
75
|
mod_hash = fio_siphash("last-modified", 13);
|
79
76
|
|
80
|
-
if (
|
81
|
-
|
82
|
-
if (
|
77
|
+
if (fio_last_tick().tv_sec > last_date_added) {
|
78
|
+
fio_lock(&date_lock);
|
79
|
+
if (fio_last_tick().tv_sec > last_date_added) { /* retest inside lock */
|
83
80
|
FIOBJ tmp = fiobj_str_buf(32);
|
84
81
|
FIOBJ old = current_date;
|
85
|
-
fiobj_str_resize(
|
86
|
-
|
87
|
-
last_date_added =
|
82
|
+
fiobj_str_resize(
|
83
|
+
tmp, http_time2str(fiobj_obj2cstr(tmp).data, fio_last_tick().tv_sec));
|
84
|
+
last_date_added = fio_last_tick().tv_sec;
|
88
85
|
current_date = tmp;
|
89
86
|
fiobj_free(old);
|
90
87
|
}
|
91
|
-
|
88
|
+
fio_unlock(&date_lock);
|
92
89
|
}
|
93
90
|
|
94
91
|
if (!fiobj_hash_get2(r->private_data.out_headers, date_hash)) {
|
@@ -119,8 +116,8 @@ static int write_header(FIOBJ o, void *w_) {
|
|
119
116
|
fiobj_each1(o, 0, write_header, w);
|
120
117
|
return 0;
|
121
118
|
}
|
122
|
-
|
123
|
-
|
119
|
+
fio_str_info_s name = fiobj_obj2cstr(w->name);
|
120
|
+
fio_str_info_s str = fiobj_obj2cstr(o);
|
124
121
|
if (!str.data)
|
125
122
|
return 0;
|
126
123
|
fiobj_str_write(w->dest, name.data, name.len);
|
@@ -159,11 +156,11 @@ int http_set_header(http_s *r, FIOBJ name, FIOBJ value) {
|
|
159
156
|
*
|
160
157
|
* Returns -1 on error and 0 on success.
|
161
158
|
*/
|
162
|
-
int http_set_header2(http_s *r,
|
163
|
-
if (HTTP_INVALID_HANDLE(r) || !n.data || !n.
|
159
|
+
int http_set_header2(http_s *r, fio_str_info_s n, fio_str_info_s v) {
|
160
|
+
if (HTTP_INVALID_HANDLE(r) || !n.data || !n.len || (v.data && !v.len))
|
164
161
|
return -1;
|
165
|
-
FIOBJ tmp = fiobj_str_new(n.data, n.
|
166
|
-
int ret = http_set_header(r, tmp, fiobj_str_new(v.data, v.
|
162
|
+
FIOBJ tmp = fiobj_str_new(n.data, n.len);
|
163
|
+
int ret = http_set_header(r, tmp, fiobj_str_new(v.data, v.len));
|
167
164
|
fiobj_free(tmp);
|
168
165
|
return ret;
|
169
166
|
}
|
@@ -179,8 +176,9 @@ int http_set_cookie(http_s *h, http_cookie_args_s cookie) {
|
|
179
176
|
HTTP_ASSERT(h, "Can't set cookie for NULL HTTP handler!");
|
180
177
|
#endif
|
181
178
|
if (HTTP_INVALID_HANDLE(h) || cookie.name_len >= 32768 ||
|
182
|
-
cookie.value_len >= 131072)
|
179
|
+
cookie.value_len >= 131072) {
|
183
180
|
return -1;
|
181
|
+
}
|
184
182
|
|
185
183
|
static int warn_illegal = 0;
|
186
184
|
|
@@ -188,7 +186,7 @@ int http_set_cookie(http_s *h, http_cookie_args_s cookie) {
|
|
188
186
|
size_t capa = cookie.name_len + cookie.value_len + 128;
|
189
187
|
size_t len = 0;
|
190
188
|
FIOBJ c = fiobj_str_buf(capa);
|
191
|
-
|
189
|
+
fio_str_info_s t = fiobj_obj2cstr(c);
|
192
190
|
if (cookie.name) {
|
193
191
|
if (cookie.name_len) {
|
194
192
|
size_t tmp = 0;
|
@@ -408,13 +406,15 @@ int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len,
|
|
408
406
|
/* create filename string */
|
409
407
|
FIOBJ filename = fiobj_str_tmp();
|
410
408
|
if (prefix && prefix_len) {
|
409
|
+
/* start with prefix path */
|
411
410
|
if (encoded && prefix[prefix_len - 1] == '/' && encoded[0] == '/')
|
412
411
|
--prefix_len;
|
413
412
|
fiobj_str_capa_assert(filename, prefix_len + encoded_len + 4);
|
414
413
|
fiobj_str_write(filename, prefix, prefix_len);
|
415
414
|
}
|
416
415
|
{
|
417
|
-
|
416
|
+
/* decode filename in cases where it's URL encoded */
|
417
|
+
fio_str_info_s tmp = fiobj_obj2cstr(filename);
|
418
418
|
if (encoded) {
|
419
419
|
char *pos = (char *)encoded;
|
420
420
|
const char *end = encoded + encoded_len;
|
@@ -427,7 +427,7 @@ int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len,
|
|
427
427
|
if (*pos == '%') {
|
428
428
|
// decode hex value
|
429
429
|
// this is a percent encoded value.
|
430
|
-
if (hex2byte(tmp.
|
430
|
+
if (hex2byte((uint8_t *)tmp.data + tmp.len, (uint8_t *)pos + 1))
|
431
431
|
return -1;
|
432
432
|
tmp.len++;
|
433
433
|
pos += 3;
|
@@ -445,12 +445,12 @@ int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len,
|
|
445
445
|
int file = -1;
|
446
446
|
uint8_t is_gz = 0;
|
447
447
|
|
448
|
-
|
448
|
+
fio_str_info_s s = fiobj_obj2cstr(filename);
|
449
449
|
{
|
450
450
|
FIOBJ tmp = fiobj_hash_get2(h->headers, accept_enc_hash);
|
451
451
|
if (!tmp)
|
452
452
|
goto no_gzip_support;
|
453
|
-
|
453
|
+
fio_str_info_s ac_str = fiobj_obj2cstr(tmp);
|
454
454
|
if (!ac_str.data || !strstr(ac_str.data, "gzip"))
|
455
455
|
goto no_gzip_support;
|
456
456
|
if (s.data[s.len - 3] != '.' || s.data[s.len - 2] != 'g' ||
|
@@ -517,7 +517,7 @@ found_file:
|
|
517
517
|
/* range ahead... */
|
518
518
|
if (FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY))
|
519
519
|
tmp = fiobj_ary_index(tmp, 0);
|
520
|
-
|
520
|
+
fio_str_info_s range = fiobj_obj2cstr(tmp);
|
521
521
|
if (!range.data || memcmp("bytes=", range.data, 6))
|
522
522
|
goto open_file;
|
523
523
|
char *pos = range.data + 6;
|
@@ -548,11 +548,14 @@ found_file:
|
|
548
548
|
}
|
549
549
|
h->status = 206;
|
550
550
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
551
|
+
{
|
552
|
+
FIOBJ cranges = fiobj_str_buf(1);
|
553
|
+
fiobj_str_printf(cranges, "bytes %lu-%lu/%lu",
|
554
|
+
(unsigned long)start_at,
|
555
|
+
(unsigned long)(start_at + length - 1),
|
556
|
+
(unsigned long)file_data.st_size);
|
557
|
+
http_set_header(h, HTTP_HEADER_CONTENT_RANGE, cranges);
|
558
|
+
}
|
556
559
|
http_set_header(h, HTTP_HEADER_ACCEPT_RANGES,
|
557
560
|
fiobj_dup(HTTP_HVALUE_BYTES));
|
558
561
|
}
|
@@ -563,8 +566,8 @@ found_file:
|
|
563
566
|
switch (s.len) {
|
564
567
|
case 7:
|
565
568
|
if (!strncasecmp("options", s.data, 7)) {
|
566
|
-
http_set_header2(h, (
|
567
|
-
(
|
569
|
+
http_set_header2(h, (fio_str_info_s){.data = "allow", .len = 5},
|
570
|
+
(fio_str_info_s){.data = "GET, HEAD", .len = 9});
|
568
571
|
h->status = 200;
|
569
572
|
http_finish(h);
|
570
573
|
return 0;
|
@@ -650,7 +653,7 @@ int http_send_error(http_s *r, size_t error) {
|
|
650
653
|
http2protocol(r)->settings->public_folder_length, buffer,
|
651
654
|
pos)) {
|
652
655
|
http_set_header(r, HTTP_HEADER_CONTENT_TYPE, http_mimetype_find("txt", 3));
|
653
|
-
|
656
|
+
fio_str_info_s t = http_status2str(error);
|
654
657
|
http_send_body(r, t.data, t.len);
|
655
658
|
}
|
656
659
|
return 0;
|
@@ -675,7 +678,7 @@ void http_finish(http_s *r) {
|
|
675
678
|
* Returns -1 on error and 0 on success.
|
676
679
|
*/
|
677
680
|
int http_push_data(http_s *r, void *data, uintptr_t length, FIOBJ mime_type) {
|
678
|
-
if (!r || !(
|
681
|
+
if (!r || !(http_fio_protocol_s *)r->private_data.flag)
|
679
682
|
return -1;
|
680
683
|
return ((http_vtable_s *)r->private_data.vtbl)
|
681
684
|
->http_push_data(r, data, length, mime_type);
|
@@ -699,44 +702,40 @@ int http_push_file(http_s *h, FIOBJ filename, FIOBJ mime_type) {
|
|
699
702
|
* Upgrades an HTTP/1.1 connection to a Websocket connection.
|
700
703
|
*/
|
701
704
|
#undef http_upgrade2ws
|
702
|
-
int http_upgrade2ws(websocket_settings_s args) {
|
703
|
-
if (!
|
705
|
+
int http_upgrade2ws(http_s *h, websocket_settings_s args) {
|
706
|
+
if (!h) {
|
704
707
|
fprintf(stderr,
|
705
708
|
"ERROR: `http_upgrade2ws` requires a valid `http_s` handle.");
|
706
709
|
goto error;
|
707
710
|
}
|
708
|
-
if (HTTP_INVALID_HANDLE(
|
711
|
+
if (HTTP_INVALID_HANDLE(h))
|
709
712
|
goto error;
|
710
|
-
return ((http_vtable_s *)
|
713
|
+
return ((http_vtable_s *)h->private_data.vtbl)->http2websocket(h, &args);
|
711
714
|
error:
|
712
715
|
if (args.on_close)
|
713
|
-
args.on_close(
|
716
|
+
args.on_close(-1, args.udata);
|
714
717
|
return -1;
|
715
718
|
}
|
716
719
|
|
717
720
|
/* *****************************************************************************
|
718
721
|
Pause / Resume
|
719
722
|
***************************************************************************** */
|
720
|
-
|
723
|
+
struct http_pause_handle_s {
|
721
724
|
uintptr_t uuid;
|
722
725
|
http_s *h;
|
723
726
|
void *udata;
|
724
727
|
void (*task)(http_s *);
|
725
728
|
void (*fallback)(void *);
|
726
|
-
}
|
729
|
+
};
|
727
730
|
|
728
731
|
/** Returns the `udata` associated with the paused opaque handle */
|
729
|
-
void *http_paused_udata_get(
|
730
|
-
const http_pause_handle_s *http = http_;
|
731
|
-
return http->udata;
|
732
|
-
}
|
732
|
+
void *http_paused_udata_get(http_pause_handle_s *http) { return http->udata; }
|
733
733
|
|
734
734
|
/**
|
735
735
|
* Sets the `udata` associated with the paused opaque handle, returning the
|
736
736
|
* old value.
|
737
737
|
*/
|
738
|
-
void *http_paused_udata_set(
|
739
|
-
http_pause_handle_s *http = http_;
|
738
|
+
void *http_paused_udata_set(http_pause_handle_s *http, void *udata) {
|
740
739
|
void *old = http->udata;
|
741
740
|
http->udata = udata;
|
742
741
|
return old;
|
@@ -749,8 +748,8 @@ static void http_pause_wrapper(void *h_, void *task_) {
|
|
749
748
|
}
|
750
749
|
|
751
750
|
/* perform the resume task within of the connection's lock */
|
752
|
-
static void http_resume_wrapper(intptr_t uuid,
|
753
|
-
|
751
|
+
static void http_resume_wrapper(intptr_t uuid, fio_protocol_s *p_, void *arg) {
|
752
|
+
http_fio_protocol_s *p = (http_fio_protocol_s *)p_;
|
754
753
|
http_pause_handle_s *http = arg;
|
755
754
|
http_s *h = http->h;
|
756
755
|
h->udata = http->udata;
|
@@ -774,39 +773,40 @@ static void http_resume_fallback_wrapper(intptr_t uuid, void *arg) {
|
|
774
773
|
/**
|
775
774
|
* Defers the request / response handling for later.
|
776
775
|
*/
|
777
|
-
void http_pause(http_s *h, void (*task)(
|
776
|
+
void http_pause(http_s *h, void (*task)(http_pause_handle_s *http)) {
|
778
777
|
if (HTTP_INVALID_HANDLE(h)) {
|
779
778
|
return;
|
780
779
|
}
|
781
|
-
|
780
|
+
http_fio_protocol_s *p = (http_fio_protocol_s *)h->private_data.flag;
|
782
781
|
http_vtable_s *vtbl = (http_vtable_s *)h->private_data.vtbl;
|
783
782
|
http_pause_handle_s *http = fio_malloc(sizeof(*http));
|
784
783
|
*http = (http_pause_handle_s){
|
785
|
-
.uuid = p->uuid,
|
784
|
+
.uuid = p->uuid,
|
785
|
+
.h = h,
|
786
|
+
.udata = h->udata,
|
786
787
|
};
|
787
788
|
vtbl->http_on_pause(h, p);
|
788
|
-
|
789
|
+
fio_defer(http_pause_wrapper, http, (void *)((uintptr_t)task));
|
789
790
|
}
|
790
791
|
|
791
792
|
/**
|
792
793
|
* Defers the request / response handling for later.
|
793
794
|
*/
|
794
|
-
void http_resume(
|
795
|
+
void http_resume(http_pause_handle_s *http, void (*task)(http_s *h),
|
795
796
|
void (*fallback)(void *udata)) {
|
796
|
-
if (!
|
797
|
+
if (!http)
|
797
798
|
return;
|
798
|
-
http_pause_handle_s *http = http_;
|
799
799
|
http->task = task;
|
800
800
|
http->fallback = fallback;
|
801
|
-
|
802
|
-
|
803
|
-
|
801
|
+
fio_defer_io_task(http->uuid, .udata = http, .type = FIO_PR_LOCK_TASK,
|
802
|
+
.task = http_resume_wrapper,
|
803
|
+
.fallback = http_resume_fallback_wrapper);
|
804
804
|
}
|
805
805
|
|
806
806
|
/**
|
807
807
|
* Hijacks the socket away from the HTTP protocol and away from facil.io.
|
808
808
|
*/
|
809
|
-
intptr_t http_hijack(http_s *h,
|
809
|
+
intptr_t http_hijack(http_s *h, fio_str_info_s *leftover) {
|
810
810
|
if (!h)
|
811
811
|
return -1;
|
812
812
|
return ((http_vtable_s *)h->private_data.vtbl)->http_hijack(h, leftover);
|
@@ -844,9 +844,9 @@ http_settings_s *http_settings_new(http_settings_s arg_settings) {
|
|
844
844
|
if (!arg_settings.max_header_size)
|
845
845
|
arg_settings.max_header_size = 32 * 1024; /* defaults to 32Kib seconds */
|
846
846
|
if (arg_settings.max_clients <= 0 ||
|
847
|
-
arg_settings.max_clients + HTTP_BUSY_UNLESS_HAS_FDS >
|
848
|
-
|
849
|
-
arg_settings.max_clients =
|
847
|
+
(size_t)(arg_settings.max_clients + HTTP_BUSY_UNLESS_HAS_FDS) >
|
848
|
+
fio_capa()) {
|
849
|
+
arg_settings.max_clients = fio_capa();
|
850
850
|
if ((ssize_t)arg_settings.max_clients - HTTP_BUSY_UNLESS_HAS_FDS > 0)
|
851
851
|
arg_settings.max_clients -= HTTP_BUSY_UNLESS_HAS_FDS;
|
852
852
|
}
|
@@ -888,19 +888,19 @@ Listening to HTTP connections
|
|
888
888
|
|
889
889
|
static void http_on_open(intptr_t uuid, void *set) {
|
890
890
|
static uint8_t at_capa;
|
891
|
-
|
892
|
-
if (
|
891
|
+
fio_timeout_set(uuid, ((http_settings_s *)set)->timeout);
|
892
|
+
if (fio_uuid2fd(uuid) >= ((http_settings_s *)set)->max_clients) {
|
893
893
|
if (!at_capa)
|
894
894
|
fprintf(stderr, "WARNING: HTTP server at capacity\n");
|
895
895
|
at_capa = 1;
|
896
896
|
http_send_error2(uuid, 503, set);
|
897
|
-
|
897
|
+
fio_close(uuid);
|
898
898
|
return;
|
899
899
|
}
|
900
900
|
at_capa = 0;
|
901
|
-
|
901
|
+
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
902
902
|
if (!pr)
|
903
|
-
|
903
|
+
fio_close(uuid);
|
904
904
|
}
|
905
905
|
|
906
906
|
static void http_on_finish(intptr_t uuid, void *set) {
|
@@ -921,8 +921,9 @@ static void http_on_finish(intptr_t uuid, void *set) {
|
|
921
921
|
* Returns -1 on error and 0 on success.
|
922
922
|
*/
|
923
923
|
#undef http_listen
|
924
|
-
|
925
|
-
|
924
|
+
intptr_t http_listen(const char *port, const char *binding,
|
925
|
+
struct http_settings_s arg_settings) {
|
926
|
+
http_lib_init();
|
926
927
|
if (arg_settings.on_request == NULL) {
|
927
928
|
fprintf(stderr, "ERROR: http_listen requires the .on_request parameter "
|
928
929
|
"to be set\n");
|
@@ -933,9 +934,9 @@ int http_listen(const char *port, const char *binding,
|
|
933
934
|
http_settings_s *settings = http_settings_new(arg_settings);
|
934
935
|
settings->is_client = 0;
|
935
936
|
|
936
|
-
return
|
937
|
-
|
938
|
-
|
937
|
+
return fio_listen(.port = port, .address = binding,
|
938
|
+
.on_finish = http_on_finish, .on_open = http_on_open,
|
939
|
+
.udata = settings);
|
939
940
|
}
|
940
941
|
/** Listens to HTTP connections at the specified `port` and `binding`. */
|
941
942
|
#define http_listen(port, binding, ...) \
|
@@ -947,25 +948,25 @@ int http_listen(const char *port, const char *binding,
|
|
947
948
|
* Returns NULL on error (i.e., connection was lost).
|
948
949
|
*/
|
949
950
|
struct http_settings_s *http_settings(http_s *r) {
|
950
|
-
return ((
|
951
|
+
return ((http_fio_protocol_s *)r->private_data.flag)->settings;
|
951
952
|
}
|
952
953
|
|
953
954
|
/**
|
954
955
|
* Returns the direct address of the connected peer (likely an intermediary).
|
955
956
|
*/
|
956
|
-
|
957
|
-
return
|
957
|
+
fio_str_info_s http_peer_addr(http_s *h) {
|
958
|
+
return fio_peer_addr(((http_fio_protocol_s *)h->private_data.flag)->uuid);
|
958
959
|
}
|
959
960
|
|
960
961
|
/* *****************************************************************************
|
961
962
|
HTTP client connections
|
962
963
|
***************************************************************************** */
|
963
964
|
|
964
|
-
static void http_on_close_client(intptr_t uuid,
|
965
|
-
|
965
|
+
static void http_on_close_client(intptr_t uuid, fio_protocol_s *protocol) {
|
966
|
+
http_fio_protocol_s *p = (http_fio_protocol_s *)protocol;
|
966
967
|
http_settings_s *set = p->settings;
|
967
|
-
void (**original)(intptr_t,
|
968
|
-
(void (**)(intptr_t,
|
968
|
+
void (**original)(intptr_t, fio_protocol_s *) =
|
969
|
+
(void (**)(intptr_t, fio_protocol_s *))(set + 1);
|
969
970
|
if (set->on_finish)
|
970
971
|
set->on_finish(set);
|
971
972
|
|
@@ -977,15 +978,15 @@ static void http_on_open_client(intptr_t uuid, void *set_) {
|
|
977
978
|
http_settings_s *set = set_;
|
978
979
|
http_s *h = set->udata;
|
979
980
|
set->udata = h->udata;
|
980
|
-
|
981
|
-
|
981
|
+
fio_timeout_set(uuid, set->timeout);
|
982
|
+
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
982
983
|
if (!pr) {
|
983
|
-
|
984
|
+
fio_close(uuid);
|
984
985
|
return;
|
985
986
|
}
|
986
987
|
{ /* store the original on_close at the end of the struct, we wrap it. */
|
987
|
-
void (**original)(intptr_t,
|
988
|
-
(void (**)(intptr_t,
|
988
|
+
void (**original)(intptr_t, fio_protocol_s *) =
|
989
|
+
(void (**)(intptr_t, fio_protocol_s *))(set + 1);
|
989
990
|
*original = pr->on_close;
|
990
991
|
pr->on_close = http_on_close_client;
|
991
992
|
}
|
@@ -1021,13 +1022,15 @@ static void http_on_client_failed(intptr_t uuid, void *set_) {
|
|
1021
1022
|
* called.
|
1022
1023
|
*/
|
1023
1024
|
#undef http_connect
|
1024
|
-
|
1025
|
+
intptr_t http_connect(const char *address,
|
1026
|
+
struct http_settings_s arg_settings) {
|
1025
1027
|
if (!arg_settings.on_response && !arg_settings.on_upgrade) {
|
1026
1028
|
fprintf(stderr, "ERROR: http_connect requires either an on_response "
|
1027
1029
|
" or an on_upgrade callback.\n");
|
1028
1030
|
errno = EINVAL;
|
1029
|
-
|
1031
|
+
goto on_error;
|
1030
1032
|
}
|
1033
|
+
http_lib_init();
|
1031
1034
|
size_t len;
|
1032
1035
|
char *a, *p;
|
1033
1036
|
uint8_t is_websocket = 0;
|
@@ -1036,8 +1039,9 @@ int http_connect(const char *address, struct http_settings_s arg_settings) {
|
|
1036
1039
|
if (!address || (len = strlen(address)) <= 5) {
|
1037
1040
|
fprintf(stderr, "ERROR: http_connect requires a valid address.\n");
|
1038
1041
|
errno = EINVAL;
|
1039
|
-
|
1042
|
+
goto on_error;
|
1040
1043
|
}
|
1044
|
+
// TODO: use http_url_parse
|
1041
1045
|
if (!strncasecmp(address, "ws", 2)) {
|
1042
1046
|
is_websocket = 1;
|
1043
1047
|
address += 2;
|
@@ -1048,7 +1052,7 @@ int http_connect(const char *address, struct http_settings_s arg_settings) {
|
|
1048
1052
|
} else {
|
1049
1053
|
fprintf(stderr, "ERROR: http_connect requires a valid address.\n");
|
1050
1054
|
errno = EINVAL;
|
1051
|
-
|
1055
|
+
goto on_error;
|
1052
1056
|
}
|
1053
1057
|
/* parse address */
|
1054
1058
|
if (address[0] == 's') {
|
@@ -1057,11 +1061,11 @@ int http_connect(const char *address, struct http_settings_s arg_settings) {
|
|
1057
1061
|
fprintf(stderr, "ERROR: http_connect doesn't support TLS/SSL "
|
1058
1062
|
"just yet.\n");
|
1059
1063
|
errno = EINVAL;
|
1060
|
-
|
1064
|
+
goto on_error;
|
1061
1065
|
} else if (len <= 3 || strncmp(address, "://", 3)) {
|
1062
1066
|
fprintf(stderr, "ERROR: http_connect requires a valid address.\n");
|
1063
1067
|
errno = EINVAL;
|
1064
|
-
|
1068
|
+
goto on_error;
|
1065
1069
|
} else {
|
1066
1070
|
len -= 3;
|
1067
1071
|
address += 3;
|
@@ -1115,24 +1119,26 @@ int http_connect(const char *address, struct http_settings_s arg_settings) {
|
|
1115
1119
|
h->status = 0;
|
1116
1120
|
h->path = path;
|
1117
1121
|
settings->udata = h;
|
1118
|
-
http_set_header2(h, (
|
1119
|
-
(
|
1122
|
+
http_set_header2(h, (fio_str_info_s){.data = "host", .len = 4},
|
1123
|
+
(fio_str_info_s){.data = a, .len = len});
|
1120
1124
|
intptr_t ret;
|
1121
1125
|
if (is_websocket) {
|
1122
1126
|
/* force HTTP/1.1 */
|
1123
|
-
ret =
|
1124
|
-
facil_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
1127
|
+
ret = fio_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
1125
1128
|
.on_connect = http_on_open_client, .udata = settings);
|
1126
1129
|
(void)0;
|
1127
1130
|
} else {
|
1128
1131
|
/* Allow for any HTTP version */
|
1129
|
-
ret =
|
1130
|
-
facil_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
1132
|
+
ret = fio_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
1131
1133
|
.on_connect = http_on_open_client, .udata = settings);
|
1132
1134
|
(void)0;
|
1133
1135
|
}
|
1134
1136
|
fio_free(a);
|
1135
1137
|
return ret;
|
1138
|
+
on_error:
|
1139
|
+
if (arg_settings.on_finish)
|
1140
|
+
arg_settings.on_finish(&arg_settings);
|
1141
|
+
return -1;
|
1136
1142
|
}
|
1137
1143
|
#define http_connect(address, ...) \
|
1138
1144
|
http_connect((address), (struct http_settings_s){__VA_ARGS__})
|
@@ -1145,13 +1151,12 @@ HTTP Websocket Connect
|
|
1145
1151
|
static void on_websocket_http_connected(http_s *h) {
|
1146
1152
|
websocket_settings_s *s = h->udata;
|
1147
1153
|
h->udata = http_settings(h)->udata = NULL;
|
1148
|
-
s->http = h;
|
1149
1154
|
if (!h->path) {
|
1150
1155
|
fprintf(stderr, "WARNING: (websocket client) path not specified in "
|
1151
1156
|
"address, assuming root!\n");
|
1152
1157
|
h->path = fiobj_str_new("/", 1);
|
1153
1158
|
}
|
1154
|
-
http_upgrade2ws(*s);
|
1159
|
+
http_upgrade2ws(h, *s);
|
1155
1160
|
fio_free(s);
|
1156
1161
|
}
|
1157
1162
|
|
@@ -1187,7 +1192,7 @@ Note:
|
|
1187
1192
|
***************************************************************************** */
|
1188
1193
|
|
1189
1194
|
static inline void http_sse_copy2str(FIOBJ dest, char *prefix, size_t pre_len,
|
1190
|
-
|
1195
|
+
fio_str_info_s data) {
|
1191
1196
|
if (!data.len)
|
1192
1197
|
return;
|
1193
1198
|
const char *stop = data.data + data.len;
|
@@ -1208,29 +1213,29 @@ static inline void http_sse_copy2str(FIOBJ dest, char *prefix, size_t pre_len,
|
|
1208
1213
|
}
|
1209
1214
|
|
1210
1215
|
/** The on message callback. the `*msg` pointer is to a temporary object. */
|
1211
|
-
static void http_sse_on_message(
|
1216
|
+
static void http_sse_on_message(fio_msg_s *msg) {
|
1212
1217
|
http_sse_internal_s *sse = msg->udata1;
|
1213
1218
|
struct http_sse_subscribe_args *args = msg->udata2;
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
facil_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
1221
|
-
return;
|
1222
|
-
}
|
1223
|
-
/* write directly to HTTP stream / connection */
|
1224
|
-
fio_cstr_s m = fiobj_obj2cstr(msg->message);
|
1225
|
-
http_sse_write(&sse->sse, .data = m);
|
1226
|
-
|
1219
|
+
/* perform a callback */
|
1220
|
+
fio_protocol_s *pr = fio_protocol_try_lock(sse->uuid, FIO_PR_LOCK_TASK);
|
1221
|
+
if (!pr)
|
1222
|
+
goto postpone;
|
1223
|
+
args->on_message(&sse->sse, msg->channel, msg->msg, args->udata);
|
1224
|
+
fio_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
1227
1225
|
return;
|
1228
1226
|
postpone:
|
1229
1227
|
if (errno == EBADF)
|
1230
1228
|
return;
|
1231
|
-
|
1229
|
+
fio_message_defer(msg);
|
1232
1230
|
return;
|
1233
1231
|
}
|
1232
|
+
|
1233
|
+
static void http_sse_on_message__direct(http_sse_s *sse, fio_str_info_s channel,
|
1234
|
+
fio_str_info_s msg, void *udata) {
|
1235
|
+
http_sse_write(sse, .data = msg);
|
1236
|
+
(void)udata;
|
1237
|
+
(void)channel;
|
1238
|
+
}
|
1234
1239
|
/** An optional callback for when a subscription is fully canceled. */
|
1235
1240
|
static void http_sse_on_unsubscribe(void *sse_, void *args_) {
|
1236
1241
|
http_sse_internal_s *sse = sse_;
|
@@ -1261,24 +1266,24 @@ uintptr_t http_sse_subscribe(http_sse_s *sse_,
|
|
1261
1266
|
http_sse_internal_s *sse = FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse_);
|
1262
1267
|
if (sse->uuid == -1)
|
1263
1268
|
return 0;
|
1269
|
+
if (!args.on_message)
|
1270
|
+
args.on_message = http_sse_on_message__direct;
|
1264
1271
|
struct http_sse_subscribe_args *udata = malloc(sizeof(*udata));
|
1265
|
-
|
1266
|
-
return 0;
|
1272
|
+
FIO_ASSERT_ALLOC(udata);
|
1267
1273
|
*udata = args;
|
1268
1274
|
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
.udata2 = udata, .use_pattern = args.use_pattern);
|
1275
|
+
fio_atomic_add(&sse->ref, 1);
|
1276
|
+
subscription_s *sub =
|
1277
|
+
fio_subscribe(.channel = args.channel, .on_message = http_sse_on_message,
|
1278
|
+
.on_unsubscribe = http_sse_on_unsubscribe, .udata1 = sse,
|
1279
|
+
.udata2 = udata, .match = args.match);
|
1275
1280
|
if (!sub)
|
1276
1281
|
return 0;
|
1277
1282
|
|
1278
|
-
|
1283
|
+
fio_lock(&sse->lock);
|
1279
1284
|
fio_ls_push(&sse->subscriptions, sub);
|
1280
1285
|
fio_ls_s *pos = sse->subscriptions.prev;
|
1281
|
-
|
1286
|
+
fio_unlock(&sse->lock);
|
1282
1287
|
return (uintptr_t)pos;
|
1283
1288
|
}
|
1284
1289
|
|
@@ -1289,11 +1294,11 @@ void http_sse_unsubscribe(http_sse_s *sse_, uintptr_t subscription) {
|
|
1289
1294
|
if (!sse_ || !subscription)
|
1290
1295
|
return;
|
1291
1296
|
http_sse_internal_s *sse = FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse_);
|
1292
|
-
|
1293
|
-
|
1297
|
+
subscription_s *sub = (subscription_s *)((fio_ls_s *)subscription)->obj;
|
1298
|
+
fio_lock(&sse->lock);
|
1294
1299
|
fio_ls_remove((fio_ls_s *)subscription);
|
1295
|
-
|
1296
|
-
|
1300
|
+
fio_unlock(&sse->lock);
|
1301
|
+
fio_unsubscribe(sub);
|
1297
1302
|
}
|
1298
1303
|
|
1299
1304
|
#undef http_upgrade2sse
|
@@ -1321,7 +1326,7 @@ void http_sse_set_timout(http_sse_s *sse_, uint8_t timeout) {
|
|
1321
1326
|
if (!sse_)
|
1322
1327
|
return;
|
1323
1328
|
http_sse_internal_s *sse = FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse_);
|
1324
|
-
|
1329
|
+
fio_timeout_set(sse->uuid, timeout);
|
1325
1330
|
}
|
1326
1331
|
|
1327
1332
|
#undef http_sse_write
|
@@ -1330,7 +1335,7 @@ void http_sse_set_timout(http_sse_s *sse_, uint8_t timeout) {
|
|
1330
1335
|
*/
|
1331
1336
|
int http_sse_write(http_sse_s *sse, struct http_sse_write_args args) {
|
1332
1337
|
if (!sse || !(args.id.len + args.data.len + args.event.len) ||
|
1333
|
-
|
1338
|
+
fio_is_closed(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid))
|
1334
1339
|
return -1;
|
1335
1340
|
FIOBJ buf;
|
1336
1341
|
{
|
@@ -1354,11 +1359,11 @@ int http_sse_write(http_sse_s *sse, struct http_sse_write_args args) {
|
|
1354
1359
|
}
|
1355
1360
|
|
1356
1361
|
/**
|
1357
|
-
* Get the connection's UUID (for
|
1362
|
+
* Get the connection's UUID (for fio_defer and similar use cases).
|
1358
1363
|
*/
|
1359
1364
|
intptr_t http_sse2uuid(http_sse_s *sse) {
|
1360
1365
|
if (!sse ||
|
1361
|
-
|
1366
|
+
fio_is_closed(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid))
|
1362
1367
|
return -1;
|
1363
1368
|
return FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid;
|
1364
1369
|
}
|
@@ -1368,7 +1373,7 @@ intptr_t http_sse2uuid(http_sse_s *sse) {
|
|
1368
1373
|
*/
|
1369
1374
|
int http_sse_close(http_sse_s *sse) {
|
1370
1375
|
if (!sse ||
|
1371
|
-
|
1376
|
+
fio_is_closed(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid))
|
1372
1377
|
return -1;
|
1373
1378
|
return FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)
|
1374
1379
|
->vtable->http_sse_close(sse);
|
@@ -1380,7 +1385,7 @@ int http_sse_close(http_sse_s *sse) {
|
|
1380
1385
|
* Returns the same object (increases a reference count, no allocation is made).
|
1381
1386
|
*/
|
1382
1387
|
http_sse_s *http_sse_dup(http_sse_s *sse) {
|
1383
|
-
|
1388
|
+
fio_atomic_add(&FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->ref, 1);
|
1384
1389
|
return sse;
|
1385
1390
|
}
|
1386
1391
|
|
@@ -1445,7 +1450,7 @@ void http_parse_query(http_s *h) {
|
|
1445
1450
|
return;
|
1446
1451
|
if (!h->params)
|
1447
1452
|
h->params = fiobj_hash_new();
|
1448
|
-
|
1453
|
+
fio_str_info_s q = fiobj_obj2cstr(h->query);
|
1449
1454
|
do {
|
1450
1455
|
char *cut = memchr(q.data, '&', q.len);
|
1451
1456
|
if (!cut)
|
@@ -1472,8 +1477,8 @@ static inline void http_parse_cookies_cookie_str(FIOBJ dest, FIOBJ str,
|
|
1472
1477
|
uint8_t is_url_encoded) {
|
1473
1478
|
if (!FIOBJ_TYPE_IS(str, FIOBJ_T_STRING))
|
1474
1479
|
return;
|
1475
|
-
|
1476
|
-
while (s.
|
1480
|
+
fio_str_info_s s = fiobj_obj2cstr(str);
|
1481
|
+
while (s.len) {
|
1477
1482
|
if (s.data[0] == ' ') {
|
1478
1483
|
++s.data;
|
1479
1484
|
--s.len;
|
@@ -1487,10 +1492,10 @@ static inline void http_parse_cookies_cookie_str(FIOBJ dest, FIOBJ str,
|
|
1487
1492
|
cut2 = s.data + s.len;
|
1488
1493
|
http_add2hash(dest, s.data, cut - s.data, cut + 1, (cut2 - (cut + 1)),
|
1489
1494
|
is_url_encoded);
|
1490
|
-
if ((size_t)((cut2 + 1) - s.data) > s.
|
1491
|
-
s.
|
1495
|
+
if ((size_t)((cut2 + 1) - s.data) > s.len)
|
1496
|
+
s.len = 0;
|
1492
1497
|
else
|
1493
|
-
s.
|
1498
|
+
s.len -= ((cut2 + 1) - s.data);
|
1494
1499
|
s.data = cut2 + 1;
|
1495
1500
|
}
|
1496
1501
|
}
|
@@ -1498,7 +1503,7 @@ static inline void http_parse_cookies_setcookie_str(FIOBJ dest, FIOBJ str,
|
|
1498
1503
|
uint8_t is_url_encoded) {
|
1499
1504
|
if (!FIOBJ_TYPE_IS(str, FIOBJ_T_STRING))
|
1500
1505
|
return;
|
1501
|
-
|
1506
|
+
fio_str_info_s s = fiobj_obj2cstr(str);
|
1502
1507
|
char *cut = memchr(s.data, '=', s.len);
|
1503
1508
|
if (!cut)
|
1504
1509
|
cut = s.data;
|
@@ -1566,10 +1571,10 @@ void http_parse_cookies(http_s *h, uint8_t is_url_encoded) {
|
|
1566
1571
|
* * "name[][key]" references a nested Hash within an array. Hash keys will be
|
1567
1572
|
* unique (repeating a key advances the hash).
|
1568
1573
|
* * These rules can be nested (i.e. "name[][key1][][key2]...")
|
1569
|
-
* * "name[][]" is an error (there's no way for the parser to
|
1570
|
-
*
|
1574
|
+
* * "name[][]" is an error (there's no way for the parser to analyze
|
1575
|
+
* dimensions)
|
1571
1576
|
*
|
1572
|
-
* Note: names can't
|
1577
|
+
* Note: names can't begin with "[" or end with "]" as these are reserved
|
1573
1578
|
* characters.
|
1574
1579
|
*/
|
1575
1580
|
int http_add2hash2(FIOBJ dest, char *name, size_t name_len, FIOBJ val,
|
@@ -1755,10 +1760,10 @@ error:
|
|
1755
1760
|
* * "name[][key]" references a nested Hash within an array. Hash keys will be
|
1756
1761
|
* unique (repeating a key advances the hash).
|
1757
1762
|
* * These rules can be nested (i.e. "name[][key1][][key2]...")
|
1758
|
-
* * "name[][]" is an error (there's no way for the parser to
|
1759
|
-
*
|
1763
|
+
* * "name[][]" is an error (there's no way for the parser to analyze
|
1764
|
+
* dimensions)
|
1760
1765
|
*
|
1761
|
-
* Note: names can't
|
1766
|
+
* Note: names can't begin with "[" or end with "]" as these are reserved
|
1762
1767
|
* characters.
|
1763
1768
|
*/
|
1764
1769
|
int http_add2hash(FIOBJ dest, char *name, size_t name_len, char *value,
|
@@ -1770,12 +1775,12 @@ int http_add2hash(FIOBJ dest, char *name, size_t name_len, char *value,
|
|
1770
1775
|
/* *****************************************************************************
|
1771
1776
|
HTTP Body Parsing
|
1772
1777
|
***************************************************************************** */
|
1773
|
-
#include
|
1778
|
+
#include <http_mime_parser.h>
|
1774
1779
|
|
1775
1780
|
typedef struct {
|
1776
1781
|
http_mime_parser_s p;
|
1777
1782
|
http_s *h;
|
1778
|
-
|
1783
|
+
fio_str_info_s buffer;
|
1779
1784
|
size_t pos;
|
1780
1785
|
size_t partial_offset;
|
1781
1786
|
size_t partial_length;
|
@@ -1797,7 +1802,7 @@ static void http_mime_parser_on_data(http_mime_parser_s *parser, void *name,
|
|
1797
1802
|
}
|
1798
1803
|
FIOBJ n = fiobj_str_new(name, name_len);
|
1799
1804
|
fiobj_str_write(n, "[data]", 6);
|
1800
|
-
|
1805
|
+
fio_str_info_s tmp = fiobj_obj2cstr(n);
|
1801
1806
|
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
1802
1807
|
value, value_len, 0);
|
1803
1808
|
fiobj_str_resize(n, name_len);
|
@@ -1826,7 +1831,8 @@ static void http_mime_parser_on_partial_start(
|
|
1826
1831
|
return;
|
1827
1832
|
|
1828
1833
|
fiobj_str_write(http_mime_parser2fio(parser)->partial_name, "[type]", 6);
|
1829
|
-
|
1834
|
+
fio_str_info_s tmp =
|
1835
|
+
fiobj_obj2cstr(http_mime_parser2fio(parser)->partial_name);
|
1830
1836
|
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
1831
1837
|
mimetype, mimetype_len, 0);
|
1832
1838
|
|
@@ -1855,7 +1861,8 @@ static void http_mime_parser_on_partial_data(http_mime_parser_s *parser,
|
|
1855
1861
|
/** Called when the partial data is complete. */
|
1856
1862
|
static void http_mime_parser_on_partial_end(http_mime_parser_s *parser) {
|
1857
1863
|
|
1858
|
-
|
1864
|
+
fio_str_info_s tmp =
|
1865
|
+
fiobj_obj2cstr(http_mime_parser2fio(parser)->partial_name);
|
1859
1866
|
http_add2hash2(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
1860
1867
|
fiobj_data_slice(http_mime_parser2fio(parser)->h->body,
|
1861
1868
|
http_mime_parser2fio(parser)->partial_offset,
|
@@ -1892,7 +1899,7 @@ int http_parse_body(http_s *h) {
|
|
1892
1899
|
if (!content_type_hash)
|
1893
1900
|
content_type_hash = fio_siphash("content-type", 12);
|
1894
1901
|
FIOBJ ct = fiobj_hash_get2(h->headers, content_type_hash);
|
1895
|
-
|
1902
|
+
fio_str_info_s content_type = fiobj_obj2cstr(ct);
|
1896
1903
|
if (content_type.len < 16)
|
1897
1904
|
return -1;
|
1898
1905
|
if (content_type.len >= 33 &&
|
@@ -1970,7 +1977,7 @@ FIOBJ http_req2str(http_s *h) {
|
|
1970
1977
|
fiobj_str_join(w.dest, h->query);
|
1971
1978
|
}
|
1972
1979
|
{
|
1973
|
-
|
1980
|
+
fio_str_info_s t = fiobj_obj2cstr(h->version);
|
1974
1981
|
if (t.len < 6 || t.data[5] != '1')
|
1975
1982
|
fiobj_str_write(w.dest, " HTTP/1.1\r\n", 10);
|
1976
1983
|
else {
|
@@ -1985,7 +1992,7 @@ FIOBJ http_req2str(http_s *h) {
|
|
1985
1992
|
fiobj_str_write(w.dest, "\r\n", 2);
|
1986
1993
|
if (h->body) {
|
1987
1994
|
// fiobj_data_seek(h->body, 0);
|
1988
|
-
//
|
1995
|
+
// fio_str_info_s t = fiobj_data_read(h->body, 0);
|
1989
1996
|
// fiobj_str_write(w.dest, t.data, t.len);
|
1990
1997
|
fiobj_str_join(w.dest, h->body);
|
1991
1998
|
}
|
@@ -2000,23 +2007,15 @@ void http_write_log(http_s *h) {
|
|
2000
2007
|
|
2001
2008
|
struct timespec start, end;
|
2002
2009
|
clock_gettime(CLOCK_REALTIME, &end);
|
2003
|
-
start =
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
sock_peer_addr(((http_protocol_s *)h->private_data.flag)->uuid);
|
2010
|
-
if (addrinfo.addrlen) {
|
2011
|
-
if (inet_ntop(
|
2012
|
-
addrinfo.addr->sa_family,
|
2013
|
-
addrinfo.addr->sa_family == AF_INET
|
2014
|
-
? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
|
2015
|
-
: (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
|
2016
|
-
buff.data, 128)) {
|
2017
|
-
buff.len = strlen(buff.data);
|
2018
|
-
}
|
2010
|
+
start = fio_last_tick();
|
2011
|
+
|
2012
|
+
{
|
2013
|
+
// TODO Guess IP address from headers (forwarded) where possible
|
2014
|
+
fio_str_info_s peer = fio_peer_addr(http2protocol(h)->uuid);
|
2015
|
+
fiobj_str_write(l, peer.data, peer.len);
|
2019
2016
|
}
|
2017
|
+
fio_str_info_s buff = fiobj_obj2cstr(l);
|
2018
|
+
|
2020
2019
|
if (buff.len == 0) {
|
2021
2020
|
memcpy(buff.data, "[unknown]", 9);
|
2022
2021
|
buff.len = 9;
|
@@ -2026,9 +2025,9 @@ void http_write_log(http_s *h) {
|
|
2026
2025
|
fiobj_str_resize(l, buff.len);
|
2027
2026
|
{
|
2028
2027
|
FIOBJ date;
|
2029
|
-
|
2028
|
+
fio_lock(&date_lock);
|
2030
2029
|
date = fiobj_dup(current_date);
|
2031
|
-
|
2030
|
+
fio_unlock(&date_lock);
|
2032
2031
|
fiobj_str_join(l, current_date);
|
2033
2032
|
fiobj_free(date);
|
2034
2033
|
}
|
@@ -2319,15 +2318,15 @@ size_t http_date2rfc2109(char *target, struct tm *tmbuf) {
|
|
2319
2318
|
/**
|
2320
2319
|
* Prints Unix time to a HTTP time formatted string.
|
2321
2320
|
*
|
2322
|
-
* This variation implements
|
2321
|
+
* This variation implements cached results for faster processing, at the
|
2323
2322
|
* price of a less accurate string.
|
2324
2323
|
*/
|
2325
2324
|
size_t http_time2str(char *target, const time_t t) {
|
2326
2325
|
/* pre-print time every 1 or 2 seconds or so. */
|
2327
2326
|
static __thread time_t cached_tick;
|
2328
2327
|
static __thread char cached_httpdate[48];
|
2329
|
-
static __thread size_t
|
2330
|
-
time_t last_tick =
|
2328
|
+
static __thread size_t cached_len;
|
2329
|
+
time_t last_tick = fio_last_tick().tv_sec;
|
2331
2330
|
if ((t | 7) < last_tick) {
|
2332
2331
|
/* this is a custom time, not "now", pass through */
|
2333
2332
|
struct tm tm;
|
@@ -2338,10 +2337,10 @@ size_t http_time2str(char *target, const time_t t) {
|
|
2338
2337
|
struct tm tm;
|
2339
2338
|
cached_tick = last_tick; /* refresh every second */
|
2340
2339
|
http_gmtime(last_tick, &tm);
|
2341
|
-
|
2340
|
+
cached_len = http_date2str(cached_httpdate, &tm);
|
2342
2341
|
}
|
2343
|
-
memcpy(target, cached_httpdate,
|
2344
|
-
return
|
2342
|
+
memcpy(target, cached_httpdate, cached_len);
|
2343
|
+
return cached_len;
|
2345
2344
|
}
|
2346
2345
|
|
2347
2346
|
/* Credit to Jonathan Leffler for the idea of a unified conditional */
|
@@ -2450,11 +2449,192 @@ ssize_t http_decode_path_unsafe(char *dest, const char *url_data) {
|
|
2450
2449
|
*pos = 0;
|
2451
2450
|
return pos - dest;
|
2452
2451
|
}
|
2452
|
+
/* *****************************************************************************
|
2453
|
+
HTTP URL parsing
|
2454
|
+
***************************************************************************** */
|
2455
|
+
|
2456
|
+
/**
|
2457
|
+
* Parses the URI returning it's components and their lengths (no decoding
|
2458
|
+
* performed, doesn't accept decoded URIs).
|
2459
|
+
*
|
2460
|
+
* The returned string are NOT NUL terminated, they are merely locations within
|
2461
|
+
* the original string.
|
2462
|
+
*
|
2463
|
+
* This function expects any of the following formats:
|
2464
|
+
*
|
2465
|
+
* * `/complete_path?query#target`
|
2466
|
+
*
|
2467
|
+
* i.e.: /index.html?page=1#list
|
2468
|
+
*
|
2469
|
+
* * `host:port/complete_path?query#target`
|
2470
|
+
*
|
2471
|
+
* i.e.:
|
2472
|
+
* example.com/index.html
|
2473
|
+
* example.com:8080/index.html
|
2474
|
+
*
|
2475
|
+
* * `schema://user:password@host:port/path?query#target`
|
2476
|
+
*
|
2477
|
+
* i.e.: http://example.com/index.html?page=1#list
|
2478
|
+
*
|
2479
|
+
* Invalid formats might produce unexpected results. No error testing performed.
|
2480
|
+
*/
|
2481
|
+
http_url_s http_url_parse(const char *url, size_t length) {
|
2482
|
+
const char *end = url + length;
|
2483
|
+
http_url_s result = {.scheme = {.data = (char *)url, .len = 0}};
|
2484
|
+
if (length == 0) {
|
2485
|
+
return result;
|
2486
|
+
}
|
2487
|
+
if (url[0] == '/') {
|
2488
|
+
result.scheme.data = NULL;
|
2489
|
+
goto parse_path;
|
2490
|
+
}
|
2491
|
+
/* test for scheme */
|
2492
|
+
while (url < end && *url != ':' && *url != '/' && *url != '@') {
|
2493
|
+
++url;
|
2494
|
+
}
|
2495
|
+
result.scheme.len = url - result.scheme.data;
|
2496
|
+
if (url + 3 >= end || url[1] != '/' || url[2] != '/') { /* host only? */
|
2497
|
+
url = result.scheme.data;
|
2498
|
+
result.scheme = (fio_str_info_s){.data = NULL};
|
2499
|
+
goto after_scheme;
|
2500
|
+
}
|
2501
|
+
|
2502
|
+
url += 3;
|
2503
|
+
|
2504
|
+
after_scheme:
|
2505
|
+
result.user.data = (char *)url;
|
2506
|
+
while (url < end) {
|
2507
|
+
switch (*url) {
|
2508
|
+
case '/':
|
2509
|
+
/* no user name or password (or port) */
|
2510
|
+
result.host.data = result.user.data;
|
2511
|
+
result.user.data = NULL;
|
2512
|
+
result.host.len = url - result.host.data;
|
2513
|
+
goto parse_path;
|
2514
|
+
break;
|
2515
|
+
case '@':
|
2516
|
+
/* user name only, no password */
|
2517
|
+
result.user.len = url - result.user.data;
|
2518
|
+
++url;
|
2519
|
+
goto parse_host;
|
2520
|
+
break;
|
2521
|
+
case ':':
|
2522
|
+
/* user name and password (or host:port...) */
|
2523
|
+
result.user.len = url - result.user.data;
|
2524
|
+
++url;
|
2525
|
+
goto parse_password;
|
2526
|
+
break;
|
2527
|
+
default:
|
2528
|
+
++url;
|
2529
|
+
break;
|
2530
|
+
}
|
2531
|
+
}
|
2532
|
+
if (url == end) { /* possibily: http://example.com */
|
2533
|
+
result.host.data = result.user.data;
|
2534
|
+
result.user.data = NULL;
|
2535
|
+
result.host.len = url - result.host.data;
|
2536
|
+
return result;
|
2537
|
+
}
|
2538
|
+
|
2539
|
+
parse_password:
|
2540
|
+
result.password.data = (char *)url;
|
2541
|
+
while (url < end) {
|
2542
|
+
switch (*url) {
|
2543
|
+
case '/':
|
2544
|
+
/* this wasn't a user:password, but host:port */
|
2545
|
+
result.host = result.user;
|
2546
|
+
result.port = result.password;
|
2547
|
+
result.port.len = url - result.port.data;
|
2548
|
+
result.user = result.password = (fio_str_info_s){.data = NULL};
|
2549
|
+
goto parse_path;
|
2550
|
+
break;
|
2551
|
+
case '@':
|
2552
|
+
/* done */
|
2553
|
+
result.password.len = url - result.password.data;
|
2554
|
+
++url;
|
2555
|
+
goto after_pass;
|
2556
|
+
default:
|
2557
|
+
++url;
|
2558
|
+
break;
|
2559
|
+
}
|
2560
|
+
}
|
2561
|
+
after_pass:
|
2562
|
+
if (url == end) { /* possibily: http://example.com:port */
|
2563
|
+
result.host = result.user;
|
2564
|
+
result.port = result.password;
|
2565
|
+
result.port.len = url - result.port.data;
|
2566
|
+
result.user = result.password = (fio_str_info_s){.data = NULL};
|
2567
|
+
return result;
|
2568
|
+
}
|
2569
|
+
parse_host:
|
2570
|
+
/* host:port parsing */
|
2571
|
+
result.host.data = (char *)url;
|
2572
|
+
while (url < end) {
|
2573
|
+
switch (*url) {
|
2574
|
+
case '/':
|
2575
|
+
/* host only */
|
2576
|
+
result.host.len = url - result.host.data;
|
2577
|
+
goto parse_path;
|
2578
|
+
break;
|
2579
|
+
case ':':
|
2580
|
+
/* done */
|
2581
|
+
result.host.len = url - result.host.data;
|
2582
|
+
++url;
|
2583
|
+
goto after_host;
|
2584
|
+
break;
|
2585
|
+
default:
|
2586
|
+
++url;
|
2587
|
+
break;
|
2588
|
+
}
|
2589
|
+
}
|
2590
|
+
after_host:
|
2591
|
+
|
2592
|
+
if (url == end) { /* scheme://host only? */
|
2593
|
+
result.host.len = end - result.host.data;
|
2594
|
+
return result;
|
2595
|
+
}
|
2596
|
+
result.port.data = (char *)url;
|
2597
|
+
while (url < end && *url != '/') {
|
2598
|
+
++url;
|
2599
|
+
}
|
2600
|
+
result.port.len = url - result.port.data;
|
2601
|
+
|
2602
|
+
if (url == end) { /* scheme://host:port only? */
|
2603
|
+
return result;
|
2604
|
+
}
|
2605
|
+
|
2606
|
+
if (url == end) { /* scheme://host only? */
|
2607
|
+
return result;
|
2608
|
+
}
|
2609
|
+
/* path, query and target parsing */
|
2610
|
+
parse_path:
|
2611
|
+
result.path.data = (char *)url;
|
2612
|
+
while (url < end && *url != '?') {
|
2613
|
+
++url;
|
2614
|
+
}
|
2615
|
+
result.path.len = url - result.path.data;
|
2616
|
+
if (url == end) {
|
2617
|
+
return result;
|
2618
|
+
}
|
2619
|
+
++url;
|
2620
|
+
result.query.data = (char *)url;
|
2621
|
+
while (url < end && *url != '#') {
|
2622
|
+
++url;
|
2623
|
+
}
|
2624
|
+
result.query.len = url - result.query.data;
|
2625
|
+
if (url == end) {
|
2626
|
+
return result;
|
2627
|
+
}
|
2628
|
+
++url;
|
2629
|
+
result.target.data = (char *)url;
|
2630
|
+
result.target.len = end - result.target.data;
|
2631
|
+
return result;
|
2632
|
+
}
|
2453
2633
|
|
2454
2634
|
/* *****************************************************************************
|
2455
2635
|
Lookup Tables / functions
|
2456
2636
|
***************************************************************************** */
|
2457
|
-
#include
|
2637
|
+
#include <fio_hashmap.h>
|
2458
2638
|
|
2459
2639
|
static fio_hash_s mime_types;
|
2460
2640
|
|
@@ -2497,11 +2677,11 @@ FIOBJ http_mimetype_find(char *file_ext, size_t file_ext_len) {
|
|
2497
2677
|
*/
|
2498
2678
|
FIOBJ http_mimetype_find2(FIOBJ url) {
|
2499
2679
|
static __thread char buffer[LONGEST_FILE_EXTENSION_LENGTH + 1];
|
2500
|
-
|
2680
|
+
fio_str_info_s ext = {.data = NULL};
|
2501
2681
|
FIOBJ mimetype;
|
2502
2682
|
if (!url)
|
2503
2683
|
goto finish;
|
2504
|
-
|
2684
|
+
fio_str_info_s tmp = fiobj_obj2cstr(url);
|
2505
2685
|
uint8_t steps = 1;
|
2506
2686
|
while (tmp.len > steps || steps >= LONGEST_FILE_EXTENSION_LENGTH) {
|
2507
2687
|
switch (tmp.data[tmp.len - steps]) {
|
@@ -2584,12 +2764,12 @@ static char invalid_cookie_value_char[256] = {
|
|
2584
2764
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
2585
2765
|
|
2586
2766
|
// clang-format off
|
2587
|
-
#define HTTP_SET_STATUS_STR(status, str) [status-100] = { .
|
2767
|
+
#define HTTP_SET_STATUS_STR(status, str) [status-100] = { .data = (str), .len = (sizeof(str) - 1) }
|
2588
2768
|
// clang-format on
|
2589
2769
|
|
2590
2770
|
/** Returns the status as a C string struct */
|
2591
|
-
|
2592
|
-
static const
|
2771
|
+
fio_str_info_s http_status2str(uintptr_t status) {
|
2772
|
+
static const fio_str_info_s status2str[] = {
|
2593
2773
|
HTTP_SET_STATUS_STR(100, "Continue"),
|
2594
2774
|
HTTP_SET_STATUS_STR(101, "Switching Protocols"),
|
2595
2775
|
HTTP_SET_STATUS_STR(102, "Processing"),
|
@@ -2655,13 +2835,25 @@ fio_cstr_s http_status2str(uintptr_t status) {
|
|
2655
2835
|
HTTP_SET_STATUS_STR(510, "Not Extended"),
|
2656
2836
|
HTTP_SET_STATUS_STR(511, "Network Authentication Required"),
|
2657
2837
|
};
|
2658
|
-
|
2838
|
+
fio_str_info_s ret = (fio_str_info_s){.len = 0, .data = NULL};
|
2659
2839
|
if (status >= 100 &&
|
2660
2840
|
(status - 100) < sizeof(status2str) / sizeof(status2str[0]))
|
2661
2841
|
ret = status2str[status - 100];
|
2662
|
-
if (!ret.
|
2842
|
+
if (!ret.data) {
|
2663
2843
|
ret = status2str[400];
|
2664
2844
|
}
|
2665
2845
|
return ret;
|
2666
2846
|
}
|
2667
2847
|
#undef HTTP_SET_STATUS_STR
|
2848
|
+
|
2849
|
+
#if DEBUG
|
2850
|
+
void http_tests(void) {
|
2851
|
+
fprintf(stderr, "=== Testing HTTP helpers\n");
|
2852
|
+
#define TEST_ASSERT(cond, ...) \
|
2853
|
+
if (!(cond)) { \
|
2854
|
+
fprintf(stderr, "* " __VA_ARGS__); \
|
2855
|
+
fprintf(stderr, "Testing failed.\n"); \
|
2856
|
+
exit(-1); \
|
2857
|
+
}
|
2858
|
+
}
|
2859
|
+
#endif
|