iodine 0.7.59 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -20
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -146
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/http.c
DELETED
|
@@ -1,2743 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright: Boaz Segev, 2016-2019
|
|
3
|
-
License: MIT
|
|
4
|
-
|
|
5
|
-
Feel free to copy, use and enjoy according to the license provided.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
#include <fio.h>
|
|
9
|
-
|
|
10
|
-
#include <http1.h>
|
|
11
|
-
#include <http_internal.h>
|
|
12
|
-
|
|
13
|
-
#include <ctype.h>
|
|
14
|
-
#include <fcntl.h>
|
|
15
|
-
#include <signal.h>
|
|
16
|
-
#include <string.h>
|
|
17
|
-
#include <sys/stat.h>
|
|
18
|
-
#include <sys/types.h>
|
|
19
|
-
#include <unistd.h>
|
|
20
|
-
|
|
21
|
-
#include <pthread.h>
|
|
22
|
-
|
|
23
|
-
#ifndef HAVE_TM_TM_ZONE
|
|
24
|
-
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
|
|
25
|
-
defined(__DragonFly__) || defined(__bsdi__) || defined(__ultrix) || \
|
|
26
|
-
(defined(__APPLE__) && defined(__MACH__)) || \
|
|
27
|
-
(defined(__sun) && !defined(__SVR4))
|
|
28
|
-
/* Known BSD systems */
|
|
29
|
-
#define HAVE_TM_TM_ZONE 1
|
|
30
|
-
#elif defined(__GLIBC__) && defined(_BSD_SOURCE)
|
|
31
|
-
/* GNU systems with _BSD_SOURCE */
|
|
32
|
-
#define HAVE_TM_TM_ZONE 1
|
|
33
|
-
#else
|
|
34
|
-
#define HAVE_TM_TM_ZONE 0
|
|
35
|
-
#endif
|
|
36
|
-
#endif
|
|
37
|
-
|
|
38
|
-
#ifndef __MINGW32__
|
|
39
|
-
/* *****************************************************************************
|
|
40
|
-
SSL/TLS patch
|
|
41
|
-
***************************************************************************** */
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Adds an ALPN protocol callback to the SSL/TLS context.
|
|
45
|
-
*
|
|
46
|
-
* The first protocol added will act as the default protocol to be selected.
|
|
47
|
-
*/
|
|
48
|
-
void __attribute__((weak))
|
|
49
|
-
fio_tls_alpn_add(void *tls, const char *protocol_name,
|
|
50
|
-
void (*callback)(intptr_t uuid, void *udata_connection,
|
|
51
|
-
void *udata_tls),
|
|
52
|
-
void *udata_tls, void (*on_cleanup)(void *udata_tls)) {
|
|
53
|
-
FIO_LOG_FATAL("HTTP SSL/TLS required but unavailable!");
|
|
54
|
-
exit(-1);
|
|
55
|
-
(void)tls;
|
|
56
|
-
(void)protocol_name;
|
|
57
|
-
(void)callback;
|
|
58
|
-
(void)on_cleanup;
|
|
59
|
-
(void)udata_tls;
|
|
60
|
-
}
|
|
61
|
-
#pragma weak fio_tls_alpn_add
|
|
62
|
-
#endif
|
|
63
|
-
|
|
64
|
-
/* *****************************************************************************
|
|
65
|
-
Small Helpers
|
|
66
|
-
***************************************************************************** */
|
|
67
|
-
static inline int hex2byte(uint8_t *dest, const uint8_t *source);
|
|
68
|
-
|
|
69
|
-
static inline void add_content_length(http_s *r, uintptr_t length) {
|
|
70
|
-
static uint64_t cl_hash = 0;
|
|
71
|
-
if (!cl_hash)
|
|
72
|
-
cl_hash = fiobj_hash_string("content-length", 14);
|
|
73
|
-
if (!fiobj_hash_get2(r->private_data.out_headers, cl_hash)) {
|
|
74
|
-
fiobj_hash_set(r->private_data.out_headers, HTTP_HEADER_CONTENT_LENGTH,
|
|
75
|
-
fiobj_num_new(length));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
static inline void add_content_type(http_s *r) {
|
|
79
|
-
static uint64_t ct_hash = 0;
|
|
80
|
-
if (!ct_hash)
|
|
81
|
-
ct_hash = fiobj_hash_string("content-type", 12);
|
|
82
|
-
if (!fiobj_hash_get2(r->private_data.out_headers, ct_hash)) {
|
|
83
|
-
fiobj_hash_set(r->private_data.out_headers, HTTP_HEADER_CONTENT_TYPE,
|
|
84
|
-
http_mimetype_find2(r->path));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
static FIOBJ current_date;
|
|
89
|
-
static time_t last_date_added;
|
|
90
|
-
static fio_lock_i date_lock;
|
|
91
|
-
static inline void add_date(http_s *r) {
|
|
92
|
-
static uint64_t date_hash = 0;
|
|
93
|
-
if (!date_hash)
|
|
94
|
-
date_hash = fiobj_hash_string("date", 4);
|
|
95
|
-
|
|
96
|
-
if (fio_last_tick().tv_sec > last_date_added) {
|
|
97
|
-
fio_lock(&date_lock);
|
|
98
|
-
if (fio_last_tick().tv_sec > last_date_added) { /* retest inside lock */
|
|
99
|
-
/* 32 chars are ok for a while, but http_time2str below has a buffer sized
|
|
100
|
-
* 48 chars and does a memcpy ... */
|
|
101
|
-
FIOBJ tmp = fiobj_str_buf(32);
|
|
102
|
-
FIOBJ old = current_date;
|
|
103
|
-
fiobj_str_resize(
|
|
104
|
-
tmp, http_time2str(fiobj_obj2cstr(tmp).data, fio_last_tick().tv_sec));
|
|
105
|
-
last_date_added = fio_last_tick().tv_sec;
|
|
106
|
-
current_date = tmp;
|
|
107
|
-
fiobj_free(old);
|
|
108
|
-
}
|
|
109
|
-
fio_unlock(&date_lock);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!fiobj_hash_get2(r->private_data.out_headers, date_hash)) {
|
|
113
|
-
fiobj_hash_set(r->private_data.out_headers, HTTP_HEADER_DATE,
|
|
114
|
-
fiobj_dup(current_date));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
struct header_writer_s {
|
|
119
|
-
FIOBJ dest;
|
|
120
|
-
FIOBJ name;
|
|
121
|
-
FIOBJ value;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
static int write_header(FIOBJ o, void *w_) {
|
|
125
|
-
struct header_writer_s *w = w_;
|
|
126
|
-
if (!o)
|
|
127
|
-
return 0;
|
|
128
|
-
if (fiobj_hash_key_in_loop()) {
|
|
129
|
-
w->name = fiobj_hash_key_in_loop();
|
|
130
|
-
}
|
|
131
|
-
if (FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY)) {
|
|
132
|
-
fiobj_each1(o, 0, write_header, w);
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
fio_str_info_s name = fiobj_obj2cstr(w->name);
|
|
136
|
-
fio_str_info_s str = fiobj_obj2cstr(o);
|
|
137
|
-
if (!str.data)
|
|
138
|
-
return 0;
|
|
139
|
-
fiobj_str_write(w->dest, name.data, name.len);
|
|
140
|
-
fiobj_str_write(w->dest, ":", 1);
|
|
141
|
-
fiobj_str_write(w->dest, str.data, str.len);
|
|
142
|
-
fiobj_str_write(w->dest, "\r\n", 2);
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
static char invalid_cookie_name_char[256];
|
|
147
|
-
|
|
148
|
-
static char invalid_cookie_value_char[256];
|
|
149
|
-
/* *****************************************************************************
|
|
150
|
-
The Request / Response type and functions
|
|
151
|
-
***************************************************************************** */
|
|
152
|
-
static const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
|
153
|
-
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Sets a response header, taking ownership of the value object, but NOT the
|
|
157
|
-
* name object (so name objects could be reused in future responses).
|
|
158
|
-
*
|
|
159
|
-
* Returns -1 on error and 0 on success.
|
|
160
|
-
*/
|
|
161
|
-
int http_set_header(http_s *r, FIOBJ name, FIOBJ value) {
|
|
162
|
-
if (HTTP_INVALID_HANDLE(r) || !name) {
|
|
163
|
-
fiobj_free(value);
|
|
164
|
-
return -1;
|
|
165
|
-
}
|
|
166
|
-
set_header_add(r->private_data.out_headers, name, value);
|
|
167
|
-
return 0;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Sets a response header.
|
|
171
|
-
*
|
|
172
|
-
* Returns -1 on error and 0 on success.
|
|
173
|
-
*/
|
|
174
|
-
int http_set_header2(http_s *r, fio_str_info_s n, fio_str_info_s v) {
|
|
175
|
-
if (HTTP_INVALID_HANDLE(r) || !n.data || !n.len || (v.data && !v.len))
|
|
176
|
-
return -1;
|
|
177
|
-
FIOBJ tmp = fiobj_str_new(n.data, n.len);
|
|
178
|
-
int ret = http_set_header(r, tmp, fiobj_str_new(v.data, v.len));
|
|
179
|
-
fiobj_free(tmp);
|
|
180
|
-
return ret;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Sets a response cookie, taking ownership of the value object, but NOT the
|
|
185
|
-
* name object (so name objects could be reused in future responses).
|
|
186
|
-
*
|
|
187
|
-
* Returns -1 on error and 0 on success.
|
|
188
|
-
*/
|
|
189
|
-
#undef http_set_cookie
|
|
190
|
-
int http_set_cookie(http_s *h, http_cookie_args_s cookie) {
|
|
191
|
-
#if DEBUG
|
|
192
|
-
FIO_ASSERT(h, "Can't set cookie for NULL HTTP handler!");
|
|
193
|
-
#endif
|
|
194
|
-
if (HTTP_INVALID_HANDLE(h) || cookie.name_len >= 32768 ||
|
|
195
|
-
cookie.value_len >= 131072) {
|
|
196
|
-
return -1;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
static int warn_illegal = 0;
|
|
200
|
-
|
|
201
|
-
/* write name and value while auto-correcting encoding issues */
|
|
202
|
-
size_t capa = cookie.name_len + cookie.value_len + 128;
|
|
203
|
-
size_t len = 0;
|
|
204
|
-
FIOBJ c = fiobj_str_buf(capa);
|
|
205
|
-
fio_str_info_s t = fiobj_obj2cstr(c);
|
|
206
|
-
|
|
207
|
-
#define copy_cookie_ch(ch_var) \
|
|
208
|
-
if (invalid_cookie_##ch_var##_char[(uint8_t)cookie.ch_var[tmp]]) { \
|
|
209
|
-
if (!warn_illegal) { \
|
|
210
|
-
++warn_illegal; \
|
|
211
|
-
FIO_LOG_WARNING("illegal char 0x%.2x in cookie " #ch_var " (in %s)\n" \
|
|
212
|
-
" automatic %% encoding applied", \
|
|
213
|
-
cookie.ch_var[tmp], cookie.ch_var); \
|
|
214
|
-
} \
|
|
215
|
-
t.data[len++] = '%'; \
|
|
216
|
-
t.data[len++] = hex_chars[((uint8_t)cookie.ch_var[tmp] >> 4) & 0x0F]; \
|
|
217
|
-
t.data[len++] = hex_chars[(uint8_t)cookie.ch_var[tmp] & 0x0F]; \
|
|
218
|
-
} else { \
|
|
219
|
-
t.data[len++] = cookie.ch_var[tmp]; \
|
|
220
|
-
} \
|
|
221
|
-
tmp += 1; \
|
|
222
|
-
if (capa <= len + 3) { \
|
|
223
|
-
capa += 32; \
|
|
224
|
-
fiobj_str_capa_assert(c, capa); \
|
|
225
|
-
t = fiobj_obj2cstr(c); \
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (cookie.name) {
|
|
229
|
-
size_t tmp = 0;
|
|
230
|
-
if (cookie.name_len) {
|
|
231
|
-
while (tmp < cookie.name_len) {
|
|
232
|
-
copy_cookie_ch(name);
|
|
233
|
-
}
|
|
234
|
-
} else {
|
|
235
|
-
while (cookie.name[tmp]) {
|
|
236
|
-
copy_cookie_ch(name);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
t.data[len++] = '=';
|
|
241
|
-
if (cookie.value) {
|
|
242
|
-
size_t tmp = 0;
|
|
243
|
-
if (cookie.value_len) {
|
|
244
|
-
while (tmp < cookie.value_len) {
|
|
245
|
-
copy_cookie_ch(value);
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
while (cookie.value[tmp]) {
|
|
249
|
-
copy_cookie_ch(value);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} else
|
|
253
|
-
cookie.max_age = -1;
|
|
254
|
-
|
|
255
|
-
if (http_settings(h) && http_settings(h)->is_client) {
|
|
256
|
-
if (!cookie.value) {
|
|
257
|
-
fiobj_free(c);
|
|
258
|
-
return -1;
|
|
259
|
-
}
|
|
260
|
-
set_header_add(h->private_data.out_headers, HTTP_HEADER_COOKIE, c);
|
|
261
|
-
return 0;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
t.data[len++] = ';';
|
|
265
|
-
t.data[len++] = ' ';
|
|
266
|
-
|
|
267
|
-
if (h->status_str || !h->status) { /* on first request status == 0 */
|
|
268
|
-
static uint64_t cookie_hash;
|
|
269
|
-
if (!cookie_hash)
|
|
270
|
-
cookie_hash = fiobj_hash_string("cookie", 6);
|
|
271
|
-
FIOBJ tmp = fiobj_hash_get2(h->private_data.out_headers, cookie_hash);
|
|
272
|
-
if (!tmp) {
|
|
273
|
-
set_header_add(h->private_data.out_headers, HTTP_HEADER_COOKIE, c);
|
|
274
|
-
} else {
|
|
275
|
-
fiobj_str_join(tmp, c);
|
|
276
|
-
fiobj_free(c);
|
|
277
|
-
}
|
|
278
|
-
return 0;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (capa <= len + 40) {
|
|
282
|
-
capa = len + 40;
|
|
283
|
-
fiobj_str_capa_assert(c, capa);
|
|
284
|
-
t = fiobj_obj2cstr(c);
|
|
285
|
-
}
|
|
286
|
-
if (cookie.max_age) {
|
|
287
|
-
memcpy(t.data + len, "Max-Age=", 8);
|
|
288
|
-
len += 8;
|
|
289
|
-
len += fio_ltoa(t.data + len, cookie.max_age, 10);
|
|
290
|
-
t.data[len++] = ';';
|
|
291
|
-
t.data[len++] = ' ';
|
|
292
|
-
}
|
|
293
|
-
fiobj_str_resize(c, len);
|
|
294
|
-
|
|
295
|
-
if (cookie.domain && cookie.domain_len) {
|
|
296
|
-
fiobj_str_write(c, "domain=", 7);
|
|
297
|
-
fiobj_str_write(c, cookie.domain, cookie.domain_len);
|
|
298
|
-
fiobj_str_write(c, ";", 1);
|
|
299
|
-
t.data[len++] = ' ';
|
|
300
|
-
}
|
|
301
|
-
if (cookie.path && cookie.path_len) {
|
|
302
|
-
fiobj_str_write(c, "path=", 5);
|
|
303
|
-
fiobj_str_write(c, cookie.path, cookie.path_len);
|
|
304
|
-
fiobj_str_write(c, ";", 1);
|
|
305
|
-
t.data[len++] = ' ';
|
|
306
|
-
}
|
|
307
|
-
if (cookie.http_only) {
|
|
308
|
-
fiobj_str_write(c, "HttpOnly;", 9);
|
|
309
|
-
}
|
|
310
|
-
if (cookie.secure) {
|
|
311
|
-
fiobj_str_write(c, "secure;", 7);
|
|
312
|
-
}
|
|
313
|
-
set_header_add(h->private_data.out_headers, HTTP_HEADER_SET_COOKIE, c);
|
|
314
|
-
return 0;
|
|
315
|
-
}
|
|
316
|
-
#define http_set_cookie(http__req__, ...) \
|
|
317
|
-
http_set_cookie((http__req__), (http_cookie_args_s){__VA_ARGS__})
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Sends the response headers and body.
|
|
321
|
-
*
|
|
322
|
-
* Returns -1 on error and 0 on success.
|
|
323
|
-
*
|
|
324
|
-
* AFTER THIS FUNCTION IS CALLED, THE `http_s` OBJECT IS NO LONGER VALID.
|
|
325
|
-
*/
|
|
326
|
-
int http_send_body(http_s *r, void *data, uintptr_t length) {
|
|
327
|
-
if (HTTP_INVALID_HANDLE(r))
|
|
328
|
-
return -1;
|
|
329
|
-
if (!length || !data) {
|
|
330
|
-
http_finish(r);
|
|
331
|
-
return 0;
|
|
332
|
-
}
|
|
333
|
-
add_content_length(r, length);
|
|
334
|
-
// add_content_type(r);
|
|
335
|
-
add_date(r);
|
|
336
|
-
return ((http_vtable_s *)r->private_data.vtbl)
|
|
337
|
-
->http_send_body(r, data, length);
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Sends the response headers and the specified file (the response's body).
|
|
341
|
-
*
|
|
342
|
-
* Returns -1 on error and 0 on success.
|
|
343
|
-
*
|
|
344
|
-
* AFTER THIS FUNCTION IS CALLED, THE `http_s` OBJECT IS NO LONGER VALID.
|
|
345
|
-
*/
|
|
346
|
-
int http_sendfile(http_s *r, int fd, uintptr_t length, uintptr_t offset) {
|
|
347
|
-
if (HTTP_INVALID_HANDLE(r)) {
|
|
348
|
-
close(fd);
|
|
349
|
-
return -1;
|
|
350
|
-
};
|
|
351
|
-
add_content_length(r, length);
|
|
352
|
-
add_content_type(r);
|
|
353
|
-
add_date(r);
|
|
354
|
-
return ((http_vtable_s *)r->private_data.vtbl)
|
|
355
|
-
->http_sendfile(r, fd, length, offset);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
static inline int http_test_encoded_path(const char *mem, size_t len) {
|
|
359
|
-
const char *pos = NULL;
|
|
360
|
-
const char *end = mem + len;
|
|
361
|
-
while (mem < end && (pos = memchr(mem, '/', (size_t)len))) {
|
|
362
|
-
len = end - pos;
|
|
363
|
-
mem = pos + 1;
|
|
364
|
-
if (pos[1] == '/')
|
|
365
|
-
return -1;
|
|
366
|
-
if (len > 3 && pos[1] == '.' && pos[2] == '.' && pos[3] == '/')
|
|
367
|
-
return -1;
|
|
368
|
-
}
|
|
369
|
-
return 0;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Sends the response headers and the specified file (the response's body).
|
|
374
|
-
*
|
|
375
|
-
* Returns -1 eton error and 0 on success.
|
|
376
|
-
*
|
|
377
|
-
* AFTER THIS FUNCTION IS CALLED, THE `http_s` OBJECT IS NO LONGER VALID.
|
|
378
|
-
*/
|
|
379
|
-
int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len,
|
|
380
|
-
const char *encoded, size_t encoded_len) {
|
|
381
|
-
if (HTTP_INVALID_HANDLE(h))
|
|
382
|
-
return -1;
|
|
383
|
-
struct stat file_data = {.st_size = 0};
|
|
384
|
-
static uint64_t accept_enc_hash = 0;
|
|
385
|
-
if (!accept_enc_hash)
|
|
386
|
-
accept_enc_hash = fiobj_hash_string("accept-encoding", 15);
|
|
387
|
-
static uint64_t range_hash = 0;
|
|
388
|
-
if (!range_hash)
|
|
389
|
-
range_hash = fiobj_hash_string("range", 5);
|
|
390
|
-
|
|
391
|
-
/* create filename string */
|
|
392
|
-
FIOBJ filename = fiobj_str_tmp();
|
|
393
|
-
if (prefix && prefix_len) {
|
|
394
|
-
/* start with prefix path */
|
|
395
|
-
if (encoded && prefix[prefix_len - 1] == '/' && encoded[0] == '/')
|
|
396
|
-
--prefix_len;
|
|
397
|
-
fiobj_str_capa_assert(filename, prefix_len + encoded_len + 4);
|
|
398
|
-
fiobj_str_write(filename, prefix, prefix_len);
|
|
399
|
-
}
|
|
400
|
-
{
|
|
401
|
-
/* decode filename in cases where it's URL encoded */
|
|
402
|
-
fio_str_info_s tmp = fiobj_obj2cstr(filename);
|
|
403
|
-
if (encoded) {
|
|
404
|
-
char *pos = (char *)encoded;
|
|
405
|
-
const char *end = encoded + encoded_len;
|
|
406
|
-
while (pos < end) {
|
|
407
|
-
if (*pos == '%') {
|
|
408
|
-
// decode hex value (this is a percent encoded value).
|
|
409
|
-
if (hex2byte((uint8_t *)tmp.data + tmp.len, (uint8_t *)pos + 1))
|
|
410
|
-
return -1;
|
|
411
|
-
tmp.len++;
|
|
412
|
-
pos += 3;
|
|
413
|
-
} else
|
|
414
|
-
tmp.data[tmp.len++] = *(pos++);
|
|
415
|
-
}
|
|
416
|
-
tmp.data[tmp.len] = 0;
|
|
417
|
-
fiobj_str_resize(filename, tmp.len);
|
|
418
|
-
/* test for path manipulations after decoding */
|
|
419
|
-
if (http_test_encoded_path(tmp.data + prefix_len, tmp.len - prefix_len))
|
|
420
|
-
return -1;
|
|
421
|
-
}
|
|
422
|
-
if (tmp.data[tmp.len - 1] == '/')
|
|
423
|
-
fiobj_str_write(filename, "index.html", 10);
|
|
424
|
-
}
|
|
425
|
-
/* test for file existance */
|
|
426
|
-
|
|
427
|
-
int file = -1;
|
|
428
|
-
uint8_t is_gz = 0;
|
|
429
|
-
|
|
430
|
-
fio_str_info_s s = fiobj_obj2cstr(filename);
|
|
431
|
-
{
|
|
432
|
-
FIOBJ tmp = fiobj_hash_get2(h->headers, accept_enc_hash);
|
|
433
|
-
if (!tmp)
|
|
434
|
-
goto no_gzip_support;
|
|
435
|
-
fio_str_info_s ac_str = fiobj_obj2cstr(tmp);
|
|
436
|
-
if (!ac_str.data || !strstr(ac_str.data, "gzip"))
|
|
437
|
-
goto no_gzip_support;
|
|
438
|
-
if (s.data[s.len - 3] != '.' || s.data[s.len - 2] != 'g' ||
|
|
439
|
-
s.data[s.len - 1] != 'z') {
|
|
440
|
-
fiobj_str_write(filename, ".gz", 3);
|
|
441
|
-
s = fiobj_obj2cstr(filename);
|
|
442
|
-
if (!stat(s.data, &file_data) &&
|
|
443
|
-
(S_ISREG(file_data.st_mode) || S_ISLNK(file_data.st_mode))) {
|
|
444
|
-
is_gz = 1;
|
|
445
|
-
goto found_file;
|
|
446
|
-
}
|
|
447
|
-
fiobj_str_resize(filename, s.len - 3);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
no_gzip_support:
|
|
451
|
-
if (stat(s.data, &file_data) ||
|
|
452
|
-
!(S_ISREG(file_data.st_mode) || S_ISLNK(file_data.st_mode)))
|
|
453
|
-
return -1;
|
|
454
|
-
found_file:
|
|
455
|
-
/* set last-modified */
|
|
456
|
-
{
|
|
457
|
-
FIOBJ tmp = fiobj_str_buf(32);
|
|
458
|
-
fiobj_str_resize(
|
|
459
|
-
tmp, http_time2str(fiobj_obj2cstr(tmp).data, file_data.st_mtime));
|
|
460
|
-
http_set_header(h, HTTP_HEADER_LAST_MODIFIED, tmp);
|
|
461
|
-
}
|
|
462
|
-
/* set cache-control */
|
|
463
|
-
http_set_header(h, HTTP_HEADER_CACHE_CONTROL, fiobj_dup(HTTP_HVALUE_MAX_AGE));
|
|
464
|
-
/* set & test etag */
|
|
465
|
-
uint64_t etag = (uint64_t)file_data.st_size;
|
|
466
|
-
etag ^= (uint64_t)file_data.st_mtime;
|
|
467
|
-
etag = fiobj_hash_string(&etag, sizeof(uint64_t));
|
|
468
|
-
FIOBJ etag_str = fiobj_str_buf(32);
|
|
469
|
-
fiobj_str_resize(etag_str,
|
|
470
|
-
fio_base64_encode(fiobj_obj2cstr(etag_str).data,
|
|
471
|
-
(void *)&etag, sizeof(uint64_t)));
|
|
472
|
-
/* set */
|
|
473
|
-
http_set_header(h, HTTP_HEADER_ETAG, etag_str);
|
|
474
|
-
/* test */
|
|
475
|
-
{
|
|
476
|
-
static uint64_t none_match_hash = 0;
|
|
477
|
-
if (!none_match_hash)
|
|
478
|
-
none_match_hash = fiobj_hash_string("if-none-match", 13);
|
|
479
|
-
FIOBJ tmp2 = fiobj_hash_get2(h->headers, none_match_hash);
|
|
480
|
-
if (tmp2 && fiobj_iseq(tmp2, etag_str)) {
|
|
481
|
-
h->status = 304;
|
|
482
|
-
http_finish(h);
|
|
483
|
-
return 0;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
/* handle range requests */
|
|
487
|
-
int64_t offset = 0;
|
|
488
|
-
int64_t length = file_data.st_size;
|
|
489
|
-
{
|
|
490
|
-
static uint64_t ifrange_hash = 0;
|
|
491
|
-
if (!ifrange_hash)
|
|
492
|
-
ifrange_hash = fiobj_hash_string("if-range", 8);
|
|
493
|
-
FIOBJ tmp = fiobj_hash_get2(h->headers, ifrange_hash);
|
|
494
|
-
if (tmp && fiobj_iseq(tmp, etag_str)) {
|
|
495
|
-
fiobj_hash_delete2(h->headers, range_hash);
|
|
496
|
-
} else {
|
|
497
|
-
tmp = fiobj_hash_get2(h->headers, range_hash);
|
|
498
|
-
if (tmp) {
|
|
499
|
-
/* range ahead... */
|
|
500
|
-
if (FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY))
|
|
501
|
-
tmp = fiobj_ary_index(tmp, 0);
|
|
502
|
-
fio_str_info_s range = fiobj_obj2cstr(tmp);
|
|
503
|
-
if (!range.data || memcmp("bytes=", range.data, 6))
|
|
504
|
-
goto open_file;
|
|
505
|
-
char *pos = range.data + 6;
|
|
506
|
-
int64_t start_at = 0, end_at = 0;
|
|
507
|
-
start_at = fio_atol(&pos);
|
|
508
|
-
if (start_at >= file_data.st_size)
|
|
509
|
-
goto open_file;
|
|
510
|
-
if (start_at >= 0) {
|
|
511
|
-
pos++;
|
|
512
|
-
end_at = fio_atol(&pos);
|
|
513
|
-
if (end_at <= 0)
|
|
514
|
-
goto open_file;
|
|
515
|
-
}
|
|
516
|
-
/* we ignore multimple ranges, only responding with the first range. */
|
|
517
|
-
if (start_at < 0) {
|
|
518
|
-
if (0 - start_at < file_data.st_size) {
|
|
519
|
-
offset = file_data.st_size - start_at;
|
|
520
|
-
length = 0 - start_at;
|
|
521
|
-
}
|
|
522
|
-
} else if (end_at) {
|
|
523
|
-
offset = start_at;
|
|
524
|
-
length = end_at - start_at + 1;
|
|
525
|
-
if (length + start_at > file_data.st_size || length <= 0)
|
|
526
|
-
length = length - start_at;
|
|
527
|
-
} else {
|
|
528
|
-
offset = start_at;
|
|
529
|
-
length = length - start_at;
|
|
530
|
-
}
|
|
531
|
-
h->status = 206;
|
|
532
|
-
|
|
533
|
-
{
|
|
534
|
-
FIOBJ cranges = fiobj_str_buf(1);
|
|
535
|
-
fiobj_str_printf(cranges, "bytes %lu-%lu/%lu",
|
|
536
|
-
(unsigned long)start_at,
|
|
537
|
-
(unsigned long)(start_at + length - 1),
|
|
538
|
-
(unsigned long)file_data.st_size);
|
|
539
|
-
http_set_header(h, HTTP_HEADER_CONTENT_RANGE, cranges);
|
|
540
|
-
}
|
|
541
|
-
http_set_header(h, HTTP_HEADER_ACCEPT_RANGES,
|
|
542
|
-
fiobj_dup(HTTP_HVALUE_BYTES));
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
/* test for an OPTIONS request or invalid methods */
|
|
547
|
-
s = fiobj_obj2cstr(h->method);
|
|
548
|
-
switch (s.len) {
|
|
549
|
-
case 7:
|
|
550
|
-
if (!strncasecmp("options", s.data, 7)) {
|
|
551
|
-
http_set_header2(h, (fio_str_info_s){.data = (char *)"allow", .len = 5},
|
|
552
|
-
(fio_str_info_s){.data = (char *)"GET, HEAD", .len = 9});
|
|
553
|
-
h->status = 200;
|
|
554
|
-
http_finish(h);
|
|
555
|
-
return 0;
|
|
556
|
-
}
|
|
557
|
-
break;
|
|
558
|
-
case 3:
|
|
559
|
-
if (!strncasecmp("get", s.data, 3))
|
|
560
|
-
goto open_file;
|
|
561
|
-
break;
|
|
562
|
-
case 4:
|
|
563
|
-
if (!strncasecmp("head", s.data, 4)) {
|
|
564
|
-
http_set_header(h, HTTP_HEADER_CONTENT_LENGTH, fiobj_num_new(length));
|
|
565
|
-
http_finish(h);
|
|
566
|
-
return 0;
|
|
567
|
-
}
|
|
568
|
-
goto open_file;
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
http_send_error(h, 403);
|
|
572
|
-
return 0;
|
|
573
|
-
open_file:
|
|
574
|
-
s = fiobj_obj2cstr(filename);
|
|
575
|
-
file = open(s.data, O_RDONLY);
|
|
576
|
-
if (file == -1) {
|
|
577
|
-
FIO_LOG_ERROR("(HTTP) couldn't open file %s!\n", s.data);
|
|
578
|
-
perror(" ");
|
|
579
|
-
http_send_error(h, 500);
|
|
580
|
-
return 0;
|
|
581
|
-
}
|
|
582
|
-
{
|
|
583
|
-
FIOBJ tmp = 0;
|
|
584
|
-
uintptr_t pos = 0;
|
|
585
|
-
if (is_gz) {
|
|
586
|
-
http_set_header(h, HTTP_HEADER_CONTENT_ENCODING,
|
|
587
|
-
fiobj_dup(HTTP_HVALUE_GZIP));
|
|
588
|
-
|
|
589
|
-
pos = s.len - 4;
|
|
590
|
-
while (pos && s.data[pos] != '.')
|
|
591
|
-
pos--;
|
|
592
|
-
pos++; /* assuming, but that's fine. */
|
|
593
|
-
tmp = http_mimetype_find(s.data + pos, s.len - pos - 3);
|
|
594
|
-
|
|
595
|
-
} else {
|
|
596
|
-
pos = s.len - 1;
|
|
597
|
-
while (pos && s.data[pos] != '.')
|
|
598
|
-
pos--;
|
|
599
|
-
pos++; /* assuming, but that's fine. */
|
|
600
|
-
tmp = http_mimetype_find(s.data + pos, s.len - pos);
|
|
601
|
-
}
|
|
602
|
-
if (tmp)
|
|
603
|
-
http_set_header(h, HTTP_HEADER_CONTENT_TYPE, tmp);
|
|
604
|
-
}
|
|
605
|
-
http_sendfile(h, file, length, offset);
|
|
606
|
-
return 0;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Sends an HTTP error response.
|
|
611
|
-
*
|
|
612
|
-
* Returns -1 on error and 0 on success.
|
|
613
|
-
*
|
|
614
|
-
* AFTER THIS FUNCTION IS CALLED, THE `http_s` OBJECT IS NO LONGER VALID.
|
|
615
|
-
*
|
|
616
|
-
* The `uuid` argument is optional and will be used only if the `http_s`
|
|
617
|
-
* argument is set to NULL.
|
|
618
|
-
*/
|
|
619
|
-
int http_send_error(http_s *r, size_t error) {
|
|
620
|
-
if (!r || !r->private_data.out_headers) {
|
|
621
|
-
return -1;
|
|
622
|
-
}
|
|
623
|
-
if (error < 100 || error >= 1000)
|
|
624
|
-
error = 500;
|
|
625
|
-
r->status = error;
|
|
626
|
-
char buffer[16];
|
|
627
|
-
buffer[0] = '/';
|
|
628
|
-
size_t pos = 1 + fio_ltoa(buffer + 1, error, 10);
|
|
629
|
-
buffer[pos++] = '.';
|
|
630
|
-
buffer[pos++] = 'h';
|
|
631
|
-
buffer[pos++] = 't';
|
|
632
|
-
buffer[pos++] = 'm';
|
|
633
|
-
buffer[pos++] = 'l';
|
|
634
|
-
buffer[pos] = 0;
|
|
635
|
-
if (http_sendfile2(r, http2protocol(r)->settings->public_folder,
|
|
636
|
-
http2protocol(r)->settings->public_folder_length, buffer,
|
|
637
|
-
pos)) {
|
|
638
|
-
http_set_header(r, HTTP_HEADER_CONTENT_TYPE,
|
|
639
|
-
http_mimetype_find((char *)"txt", 3));
|
|
640
|
-
fio_str_info_s t = http_status2str(error);
|
|
641
|
-
http_send_body(r, t.data, t.len);
|
|
642
|
-
}
|
|
643
|
-
return 0;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
/**
|
|
647
|
-
* Sends the response headers for a header only response.
|
|
648
|
-
*
|
|
649
|
-
* AFTER THIS FUNCTION IS CALLED, THE `http_s` OBJECT IS NO LONGER VALID.
|
|
650
|
-
*/
|
|
651
|
-
void http_finish(http_s *r) {
|
|
652
|
-
if (!r || !r->private_data.vtbl) {
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
add_content_length(r, 0);
|
|
656
|
-
add_date(r);
|
|
657
|
-
((http_vtable_s *)r->private_data.vtbl)->http_finish(r);
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Pushes a data response when supported (HTTP/2 only).
|
|
661
|
-
*
|
|
662
|
-
* Returns -1 on error and 0 on success.
|
|
663
|
-
*/
|
|
664
|
-
int http_push_data(http_s *r, void *data, uintptr_t length, FIOBJ mime_type) {
|
|
665
|
-
if (!r || !(http_fio_protocol_s *)r->private_data.flag)
|
|
666
|
-
return -1;
|
|
667
|
-
return ((http_vtable_s *)r->private_data.vtbl)
|
|
668
|
-
->http_push_data(r, data, length, mime_type);
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Pushes a file response when supported (HTTP/2 only).
|
|
672
|
-
*
|
|
673
|
-
* If `mime_type` is NULL, an attempt at automatic detection using
|
|
674
|
-
* `filename` will be made.
|
|
675
|
-
*
|
|
676
|
-
* Returns -1 on error and 0 on success.
|
|
677
|
-
*/
|
|
678
|
-
int http_push_file(http_s *h, FIOBJ filename, FIOBJ mime_type) {
|
|
679
|
-
if (HTTP_INVALID_HANDLE(h))
|
|
680
|
-
return -1;
|
|
681
|
-
return ((http_vtable_s *)h->private_data.vtbl)
|
|
682
|
-
->http_push_file(h, filename, mime_type);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
/**
|
|
686
|
-
* Upgrades an HTTP/1.1 connection to a Websocket connection.
|
|
687
|
-
*/
|
|
688
|
-
#undef http_upgrade2ws
|
|
689
|
-
int http_upgrade2ws(http_s *h, websocket_settings_s args) {
|
|
690
|
-
if (!h) {
|
|
691
|
-
FIO_LOG_ERROR("`http_upgrade2ws` requires a valid `http_s` handle.");
|
|
692
|
-
goto error;
|
|
693
|
-
}
|
|
694
|
-
if (HTTP_INVALID_HANDLE(h))
|
|
695
|
-
goto error;
|
|
696
|
-
return ((http_vtable_s *)h->private_data.vtbl)->http2websocket(h, &args);
|
|
697
|
-
error:
|
|
698
|
-
if (args.on_close)
|
|
699
|
-
args.on_close(-1, args.udata);
|
|
700
|
-
return -1;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/* *****************************************************************************
|
|
704
|
-
Pause / Resume
|
|
705
|
-
***************************************************************************** */
|
|
706
|
-
struct http_pause_handle_s {
|
|
707
|
-
uintptr_t uuid;
|
|
708
|
-
http_s *h;
|
|
709
|
-
void *udata;
|
|
710
|
-
void (*task)(http_s *);
|
|
711
|
-
void (*fallback)(void *);
|
|
712
|
-
};
|
|
713
|
-
|
|
714
|
-
/** Returns the `udata` associated with the paused opaque handle */
|
|
715
|
-
void *http_paused_udata_get(http_pause_handle_s *http) { return http->udata; }
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* Sets the `udata` associated with the paused opaque handle, returning the
|
|
719
|
-
* old value.
|
|
720
|
-
*/
|
|
721
|
-
void *http_paused_udata_set(http_pause_handle_s *http, void *udata) {
|
|
722
|
-
void *old = http->udata;
|
|
723
|
-
http->udata = udata;
|
|
724
|
-
return old;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/* perform the pause task outside of the connection's lock */
|
|
728
|
-
static void http_pause_wrapper(void *h_, void *task_) {
|
|
729
|
-
void (*task)(void *h) = (void (*)(void *h))((uintptr_t)task_);
|
|
730
|
-
task(h_);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/* perform the resume task within of the connection's lock */
|
|
734
|
-
static void http_resume_wrapper(intptr_t uuid, fio_protocol_s *p_, void *arg) {
|
|
735
|
-
http_fio_protocol_s *p = (http_fio_protocol_s *)p_;
|
|
736
|
-
http_pause_handle_s *http = arg;
|
|
737
|
-
http_s *h = http->h;
|
|
738
|
-
h->udata = http->udata;
|
|
739
|
-
http_vtable_s *vtbl = (http_vtable_s *)h->private_data.vtbl;
|
|
740
|
-
if (http->task)
|
|
741
|
-
http->task(h);
|
|
742
|
-
vtbl->http_on_resume(h, p);
|
|
743
|
-
fio_free(http);
|
|
744
|
-
(void)uuid;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
/* perform the resume task fallback */
|
|
748
|
-
static void http_resume_fallback_wrapper(intptr_t uuid, void *arg) {
|
|
749
|
-
http_pause_handle_s *http = arg;
|
|
750
|
-
if (http->fallback)
|
|
751
|
-
http->fallback(http->udata);
|
|
752
|
-
fio_free(http);
|
|
753
|
-
(void)uuid;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Defers the request / response handling for later.
|
|
758
|
-
*/
|
|
759
|
-
void http_pause(http_s *h, void (*task)(http_pause_handle_s *http)) {
|
|
760
|
-
if (HTTP_INVALID_HANDLE(h)) {
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
http_fio_protocol_s *p = (http_fio_protocol_s *)h->private_data.flag;
|
|
764
|
-
http_vtable_s *vtbl = (http_vtable_s *)h->private_data.vtbl;
|
|
765
|
-
http_pause_handle_s *http = fio_malloc(sizeof(*http));
|
|
766
|
-
FIO_ASSERT_ALLOC(http);
|
|
767
|
-
*http = (http_pause_handle_s){
|
|
768
|
-
.uuid = p->uuid,
|
|
769
|
-
.h = h,
|
|
770
|
-
.udata = h->udata,
|
|
771
|
-
};
|
|
772
|
-
vtbl->http_on_pause(h, p);
|
|
773
|
-
fio_defer(http_pause_wrapper, http, (void *)((uintptr_t)task));
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
/**
|
|
777
|
-
* Defers the request / response handling for later.
|
|
778
|
-
*/
|
|
779
|
-
void http_resume(http_pause_handle_s *http, void (*task)(http_s *h),
|
|
780
|
-
void (*fallback)(void *udata)) {
|
|
781
|
-
if (!http)
|
|
782
|
-
return;
|
|
783
|
-
http->task = task;
|
|
784
|
-
http->fallback = fallback;
|
|
785
|
-
fio_defer_io_task(http->uuid, .udata = http, .type = FIO_PR_LOCK_TASK,
|
|
786
|
-
.task = http_resume_wrapper,
|
|
787
|
-
.fallback = http_resume_fallback_wrapper);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
/**
|
|
791
|
-
* Hijacks the socket away from the HTTP protocol and away from facil.io.
|
|
792
|
-
*/
|
|
793
|
-
intptr_t http_hijack(http_s *h, fio_str_info_s *leftover) {
|
|
794
|
-
if (!h)
|
|
795
|
-
return -1;
|
|
796
|
-
return ((http_vtable_s *)h->private_data.vtbl)->http_hijack(h, leftover);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
/* *****************************************************************************
|
|
800
|
-
Setting the default settings and allocating a persistent copy
|
|
801
|
-
***************************************************************************** */
|
|
802
|
-
|
|
803
|
-
static void http_on_request_fallback(http_s *h) { http_send_error(h, 404); }
|
|
804
|
-
static void http_on_upgrade_fallback(http_s *h, char *p, size_t i) {
|
|
805
|
-
http_send_error(h, 400);
|
|
806
|
-
(void)p;
|
|
807
|
-
(void)i;
|
|
808
|
-
}
|
|
809
|
-
static void http_on_response_fallback(http_s *h) { http_send_error(h, 400); }
|
|
810
|
-
|
|
811
|
-
static http_settings_s *http_settings_new(http_settings_s arg_settings) {
|
|
812
|
-
/* TODO: improve locality by unifying malloc to a single call */
|
|
813
|
-
if (!arg_settings.on_request)
|
|
814
|
-
arg_settings.on_request = http_on_request_fallback;
|
|
815
|
-
if (!arg_settings.on_response)
|
|
816
|
-
arg_settings.on_response = http_on_response_fallback;
|
|
817
|
-
if (!arg_settings.on_upgrade)
|
|
818
|
-
arg_settings.on_upgrade = http_on_upgrade_fallback;
|
|
819
|
-
|
|
820
|
-
if (!arg_settings.max_body_size)
|
|
821
|
-
arg_settings.max_body_size = HTTP_DEFAULT_BODY_LIMIT;
|
|
822
|
-
if (!arg_settings.timeout)
|
|
823
|
-
arg_settings.timeout = 40;
|
|
824
|
-
if (!arg_settings.ws_max_msg_size)
|
|
825
|
-
arg_settings.ws_max_msg_size = 262144; /** defaults to ~250KB */
|
|
826
|
-
if (!arg_settings.ws_timeout)
|
|
827
|
-
arg_settings.ws_timeout = 40; /* defaults to 40 seconds */
|
|
828
|
-
if (!arg_settings.max_header_size)
|
|
829
|
-
arg_settings.max_header_size = 32 * 1024; /* defaults to 32Kib seconds */
|
|
830
|
-
if (arg_settings.max_clients <= 0 ||
|
|
831
|
-
(size_t)(arg_settings.max_clients + HTTP_BUSY_UNLESS_HAS_FDS) >
|
|
832
|
-
fio_capa()) {
|
|
833
|
-
arg_settings.max_clients = fio_capa();
|
|
834
|
-
if ((ssize_t)arg_settings.max_clients - HTTP_BUSY_UNLESS_HAS_FDS > 0)
|
|
835
|
-
arg_settings.max_clients -= HTTP_BUSY_UNLESS_HAS_FDS;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
http_settings_s *settings = malloc(sizeof(*settings) + sizeof(void *));
|
|
839
|
-
*settings = arg_settings;
|
|
840
|
-
|
|
841
|
-
if (settings->public_folder) {
|
|
842
|
-
settings->public_folder_length = strlen(settings->public_folder);
|
|
843
|
-
if (settings->public_folder[0] == '~' &&
|
|
844
|
-
settings->public_folder[1] == '/' && getenv("HOME")) {
|
|
845
|
-
char *home = getenv("HOME");
|
|
846
|
-
size_t home_len = strlen(home);
|
|
847
|
-
char *tmp = malloc(settings->public_folder_length + home_len + 1);
|
|
848
|
-
FIO_ASSERT_ALLOC(tmp);
|
|
849
|
-
memcpy(tmp, home, home_len);
|
|
850
|
-
if (home[home_len - 1] == '/')
|
|
851
|
-
--home_len;
|
|
852
|
-
memcpy(tmp + home_len, settings->public_folder + 1,
|
|
853
|
-
settings->public_folder_length); // copy also the NULL
|
|
854
|
-
settings->public_folder = tmp;
|
|
855
|
-
settings->public_folder_length = strlen(settings->public_folder);
|
|
856
|
-
} else {
|
|
857
|
-
settings->public_folder = malloc(settings->public_folder_length + 1);
|
|
858
|
-
FIO_ASSERT_ALLOC(settings->public_folder);
|
|
859
|
-
memcpy((void *)settings->public_folder, arg_settings.public_folder,
|
|
860
|
-
settings->public_folder_length);
|
|
861
|
-
((uint8_t *)settings->public_folder)[settings->public_folder_length] = 0;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
return settings;
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
static void http_settings_free(http_settings_s *s) {
|
|
868
|
-
free((void *)s->public_folder);
|
|
869
|
-
free(s);
|
|
870
|
-
}
|
|
871
|
-
/* *****************************************************************************
|
|
872
|
-
Listening to HTTP connections
|
|
873
|
-
***************************************************************************** */
|
|
874
|
-
|
|
875
|
-
static uint8_t fio_http_at_capa = 0;
|
|
876
|
-
|
|
877
|
-
static void http_on_server_protocol_http1(intptr_t uuid, void *set,
|
|
878
|
-
void *ignr_) {
|
|
879
|
-
if ((unsigned int)fio_uuid2fd(uuid) >=
|
|
880
|
-
((http_settings_s *)set)->max_clients) {
|
|
881
|
-
if (fio_uuid2fd(uuid) != -1) {
|
|
882
|
-
if (!fio_http_at_capa)
|
|
883
|
-
FIO_LOG_WARNING("HTTP server at capacity");
|
|
884
|
-
fio_http_at_capa = 1;
|
|
885
|
-
http_send_error2(503, uuid, set);
|
|
886
|
-
fio_close(uuid);
|
|
887
|
-
}
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
fio_http_at_capa = 0;
|
|
891
|
-
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
|
892
|
-
if (!pr)
|
|
893
|
-
fio_close(uuid);
|
|
894
|
-
else
|
|
895
|
-
fio_timeout_set(uuid, ((http_settings_s *)set)->timeout);
|
|
896
|
-
(void)ignr_;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
static void http_on_open(intptr_t uuid, void *set) {
|
|
900
|
-
http_on_server_protocol_http1(uuid, set, NULL);
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
static void http_on_finish(intptr_t uuid, void *set) {
|
|
904
|
-
http_settings_s *settings = set;
|
|
905
|
-
|
|
906
|
-
if (settings->on_finish)
|
|
907
|
-
settings->on_finish(settings);
|
|
908
|
-
|
|
909
|
-
http_settings_free(settings);
|
|
910
|
-
(void)uuid;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
/**
|
|
914
|
-
* Listens to HTTP connections at the specified `port`.
|
|
915
|
-
*
|
|
916
|
-
* Leave as NULL to ignore IP binding.
|
|
917
|
-
*
|
|
918
|
-
* Returns -1 on error and 0 on success.
|
|
919
|
-
*/
|
|
920
|
-
#undef http_listen
|
|
921
|
-
intptr_t http_listen(const char *port, const char *binding,
|
|
922
|
-
struct http_settings_s arg_settings) {
|
|
923
|
-
if (arg_settings.on_request == NULL) {
|
|
924
|
-
FIO_LOG_ERROR("http_listen requires the .on_request parameter "
|
|
925
|
-
"to be set\n");
|
|
926
|
-
kill(0, SIGINT);
|
|
927
|
-
exit(11);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
http_settings_s *settings = http_settings_new(arg_settings);
|
|
931
|
-
settings->is_client = 0;
|
|
932
|
-
#ifndef __MINGW32__
|
|
933
|
-
if (settings->tls) {
|
|
934
|
-
fio_tls_alpn_add(settings->tls, "http/1.1", http_on_server_protocol_http1,
|
|
935
|
-
NULL, NULL);
|
|
936
|
-
}
|
|
937
|
-
#endif
|
|
938
|
-
|
|
939
|
-
return fio_listen(.port = port, .address = binding, .tls = arg_settings.tls,
|
|
940
|
-
.on_finish = http_on_finish, .on_open = http_on_open,
|
|
941
|
-
.udata = settings);
|
|
942
|
-
}
|
|
943
|
-
/** Listens to HTTP connections at the specified `port` and `binding`. */
|
|
944
|
-
#define http_listen(port, binding, ...) \
|
|
945
|
-
http_listen((port), (binding), (struct http_settings_s)(__VA_ARGS__))
|
|
946
|
-
|
|
947
|
-
/**
|
|
948
|
-
* Returns the settings used to setup the connection.
|
|
949
|
-
*
|
|
950
|
-
* Returns NULL on error (i.e., connection was lost).
|
|
951
|
-
*/
|
|
952
|
-
struct http_settings_s *http_settings(http_s *r) {
|
|
953
|
-
return ((http_fio_protocol_s *)r->private_data.flag)->settings;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/**
|
|
957
|
-
* Returns the direct address of the connected peer (likely an intermediary).
|
|
958
|
-
*/
|
|
959
|
-
fio_str_info_s http_peer_addr(http_s *h) {
|
|
960
|
-
return fio_peer_addr(((http_fio_protocol_s *)h->private_data.flag)->uuid);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
/* *****************************************************************************
|
|
964
|
-
HTTP client connections
|
|
965
|
-
***************************************************************************** */
|
|
966
|
-
|
|
967
|
-
static void http_on_close_client(intptr_t uuid, fio_protocol_s *protocol) {
|
|
968
|
-
http_fio_protocol_s *p = (http_fio_protocol_s *)protocol;
|
|
969
|
-
http_settings_s *set = p->settings;
|
|
970
|
-
void (**original)(intptr_t, fio_protocol_s *) =
|
|
971
|
-
(void (**)(intptr_t, fio_protocol_s *))(set + 1);
|
|
972
|
-
if (set->on_finish)
|
|
973
|
-
set->on_finish(set);
|
|
974
|
-
|
|
975
|
-
original[0](uuid, protocol);
|
|
976
|
-
http_settings_free(set);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
static void http_on_open_client_perform(http_settings_s *set) {
|
|
980
|
-
http_s *h = set->udata;
|
|
981
|
-
set->on_response(h);
|
|
982
|
-
}
|
|
983
|
-
static void http_on_open_client_http1(intptr_t uuid, void *set_,
|
|
984
|
-
void *ignore_) {
|
|
985
|
-
http_settings_s *set = set_;
|
|
986
|
-
http_s *h = set->udata;
|
|
987
|
-
fio_timeout_set(uuid, set->timeout);
|
|
988
|
-
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
|
989
|
-
if (!pr) {
|
|
990
|
-
fio_close(uuid);
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
{ /* store the original on_close at the end of the struct, we wrap it. */
|
|
994
|
-
void (**original)(intptr_t, fio_protocol_s *) =
|
|
995
|
-
(void (**)(intptr_t, fio_protocol_s *))(set + 1);
|
|
996
|
-
*original = pr->on_close;
|
|
997
|
-
pr->on_close = http_on_close_client;
|
|
998
|
-
}
|
|
999
|
-
h->private_data.flag = (uintptr_t)pr;
|
|
1000
|
-
h->private_data.vtbl = http1_vtable();
|
|
1001
|
-
http_on_open_client_perform(set);
|
|
1002
|
-
(void)ignore_;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
static void http_on_open_client(intptr_t uuid, void *set_) {
|
|
1006
|
-
http_on_open_client_http1(uuid, set_, NULL);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
static void http_on_client_failed(intptr_t uuid, void *set_) {
|
|
1010
|
-
http_settings_s *set = set_;
|
|
1011
|
-
http_s *h = set->udata;
|
|
1012
|
-
set->udata = h->udata;
|
|
1013
|
-
http_s_destroy(h, 0);
|
|
1014
|
-
fio_free(h);
|
|
1015
|
-
if (set->on_finish)
|
|
1016
|
-
set->on_finish(set);
|
|
1017
|
-
http_settings_free(set);
|
|
1018
|
-
(void)uuid;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
intptr_t http_connect__(void); /* sublime text marker */
|
|
1022
|
-
/**
|
|
1023
|
-
* Connects to an HTTP server as a client.
|
|
1024
|
-
*
|
|
1025
|
-
* Upon a successful connection, the `on_response` callback is called with an
|
|
1026
|
-
* empty `http_s*` handler (status == 0). Use the same API to set it's content
|
|
1027
|
-
* and send the request to the server. The next`on_response` will contain the
|
|
1028
|
-
* response.
|
|
1029
|
-
*
|
|
1030
|
-
* `address` should contain a full URL style address for the server. i.e.:
|
|
1031
|
-
* "http:/www.example.com:8080/"
|
|
1032
|
-
*
|
|
1033
|
-
* Returns -1 on error and 0 on success. the `on_finish` callback is always
|
|
1034
|
-
* called.
|
|
1035
|
-
*/
|
|
1036
|
-
intptr_t http_connect FIO_IGNORE_MACRO(const char *url,
|
|
1037
|
-
const char *unix_address,
|
|
1038
|
-
struct http_settings_s arg_settings) {
|
|
1039
|
-
if (!arg_settings.on_response && !arg_settings.on_upgrade) {
|
|
1040
|
-
FIO_LOG_ERROR("http_connect requires either an on_response "
|
|
1041
|
-
" or an on_upgrade callback.\n");
|
|
1042
|
-
errno = EINVAL;
|
|
1043
|
-
goto on_error;
|
|
1044
|
-
}
|
|
1045
|
-
size_t len = 0, h_len = 0;
|
|
1046
|
-
char *a = NULL, *p = NULL, *host = NULL;
|
|
1047
|
-
uint8_t is_websocket = 0;
|
|
1048
|
-
uint8_t is_secure = 0;
|
|
1049
|
-
FIOBJ path = FIOBJ_INVALID;
|
|
1050
|
-
if (!url && !unix_address) {
|
|
1051
|
-
FIO_LOG_ERROR("http_connect requires a valid address.");
|
|
1052
|
-
errno = EINVAL;
|
|
1053
|
-
goto on_error;
|
|
1054
|
-
}
|
|
1055
|
-
if (url) {
|
|
1056
|
-
fio_url_s u = fio_url_parse(url, strlen(url));
|
|
1057
|
-
if (u.scheme.data &&
|
|
1058
|
-
(u.scheme.len == 2 || (u.scheme.len == 3 && u.scheme.data[2] == 's')) &&
|
|
1059
|
-
u.scheme.data[0] == 'w' && u.scheme.data[1] == 's') {
|
|
1060
|
-
is_websocket = 1;
|
|
1061
|
-
is_secure = (u.scheme.len == 3);
|
|
1062
|
-
} else if (u.scheme.data &&
|
|
1063
|
-
(u.scheme.len == 4 ||
|
|
1064
|
-
(u.scheme.len == 5 && u.scheme.data[4] == 's')) &&
|
|
1065
|
-
u.scheme.data[0] == 'h' && u.scheme.data[1] == 't' &&
|
|
1066
|
-
u.scheme.data[2] == 't' && u.scheme.data[3] == 'p') {
|
|
1067
|
-
is_secure = (u.scheme.len == 5);
|
|
1068
|
-
}
|
|
1069
|
-
if (is_secure && !arg_settings.tls) {
|
|
1070
|
-
FIO_LOG_ERROR("Secure connections (%.*s) require a TLS object.",
|
|
1071
|
-
(int)u.scheme.len, u.scheme.data);
|
|
1072
|
-
errno = EINVAL;
|
|
1073
|
-
goto on_error;
|
|
1074
|
-
}
|
|
1075
|
-
if (u.path.data) {
|
|
1076
|
-
path = fiobj_str_new(
|
|
1077
|
-
u.path.data, strlen(u.path.data)); /* copy query and target as well */
|
|
1078
|
-
}
|
|
1079
|
-
if (unix_address) {
|
|
1080
|
-
a = (char *)unix_address;
|
|
1081
|
-
h_len = len = strlen(a);
|
|
1082
|
-
host = a;
|
|
1083
|
-
} else {
|
|
1084
|
-
if (!u.host.data) {
|
|
1085
|
-
FIO_LOG_ERROR("http_connect requires a valid address.");
|
|
1086
|
-
errno = EINVAL;
|
|
1087
|
-
goto on_error;
|
|
1088
|
-
}
|
|
1089
|
-
/***** no more error handling, since memory is allocated *****/
|
|
1090
|
-
/* copy address */
|
|
1091
|
-
a = fio_malloc(u.host.len + 1);
|
|
1092
|
-
memcpy(a, u.host.data, u.host.len);
|
|
1093
|
-
a[u.host.len] = 0;
|
|
1094
|
-
len = u.host.len;
|
|
1095
|
-
/* copy port */
|
|
1096
|
-
if (u.port.data) {
|
|
1097
|
-
p = fio_malloc(u.port.len + 1);
|
|
1098
|
-
memcpy(p, u.port.data, u.port.len);
|
|
1099
|
-
p[u.port.len] = 0;
|
|
1100
|
-
} else if (is_secure) {
|
|
1101
|
-
p = fio_malloc(3 + 1);
|
|
1102
|
-
memcpy(p, "443", 3);
|
|
1103
|
-
p[3] = 0;
|
|
1104
|
-
} else {
|
|
1105
|
-
p = fio_malloc(2 + 1);
|
|
1106
|
-
memcpy(p, "80", 2);
|
|
1107
|
-
p[2] = 0;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
if (u.host.data) {
|
|
1111
|
-
host = u.host.data;
|
|
1112
|
-
h_len = u.host.len;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
/* set settings */
|
|
1117
|
-
if (!arg_settings.timeout)
|
|
1118
|
-
arg_settings.timeout = 30;
|
|
1119
|
-
http_settings_s *settings = http_settings_new(arg_settings);
|
|
1120
|
-
settings->is_client = 1;
|
|
1121
|
-
// if (settings->tls) {
|
|
1122
|
-
// fio_tls_alpn_add(settings->tls, "http/1.1", http_on_open_client_http1,
|
|
1123
|
-
// NULL, NULL);
|
|
1124
|
-
// }
|
|
1125
|
-
|
|
1126
|
-
if (!arg_settings.ws_timeout)
|
|
1127
|
-
settings->ws_timeout = 0; /* allow server to dictate timeout */
|
|
1128
|
-
if (!arg_settings.timeout)
|
|
1129
|
-
settings->timeout = 0; /* allow server to dictate timeout */
|
|
1130
|
-
http_s *h = fio_malloc(sizeof(*h));
|
|
1131
|
-
FIO_ASSERT(h, "HTTP Client handler allocation failed");
|
|
1132
|
-
http_s_new(h, 0, http1_vtable());
|
|
1133
|
-
h->udata = arg_settings.udata;
|
|
1134
|
-
h->status = 0;
|
|
1135
|
-
h->path = path;
|
|
1136
|
-
settings->udata = h;
|
|
1137
|
-
settings->tls = arg_settings.tls;
|
|
1138
|
-
if (host)
|
|
1139
|
-
http_set_header2(h, (fio_str_info_s){.data = (char *)"host", .len = 4},
|
|
1140
|
-
(fio_str_info_s){.data = host, .len = h_len});
|
|
1141
|
-
intptr_t ret;
|
|
1142
|
-
if (is_websocket) {
|
|
1143
|
-
/* force HTTP/1.1 */
|
|
1144
|
-
ret = fio_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
|
1145
|
-
.on_connect = http_on_open_client, .udata = settings,
|
|
1146
|
-
.tls = arg_settings.tls);
|
|
1147
|
-
(void)0;
|
|
1148
|
-
} else {
|
|
1149
|
-
/* Allow for any HTTP version */
|
|
1150
|
-
ret = fio_connect(.address = a, .port = p, .on_fail = http_on_client_failed,
|
|
1151
|
-
.on_connect = http_on_open_client, .udata = settings,
|
|
1152
|
-
.tls = arg_settings.tls);
|
|
1153
|
-
(void)0;
|
|
1154
|
-
}
|
|
1155
|
-
if (a != unix_address)
|
|
1156
|
-
fio_free(a);
|
|
1157
|
-
fio_free(p);
|
|
1158
|
-
return ret;
|
|
1159
|
-
on_error:
|
|
1160
|
-
if (arg_settings.on_finish)
|
|
1161
|
-
arg_settings.on_finish(&arg_settings);
|
|
1162
|
-
return -1;
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
/* *****************************************************************************
|
|
1166
|
-
HTTP Websocket Connect
|
|
1167
|
-
***************************************************************************** */
|
|
1168
|
-
|
|
1169
|
-
#undef http_upgrade2ws
|
|
1170
|
-
static void on_websocket_http_connected(http_s *h) {
|
|
1171
|
-
websocket_settings_s *s = h->udata;
|
|
1172
|
-
h->udata = http_settings(h)->udata = NULL;
|
|
1173
|
-
if (!h->path) {
|
|
1174
|
-
FIO_LOG_WARNING("(websocket client) path not specified in "
|
|
1175
|
-
"address, assuming root!");
|
|
1176
|
-
h->path = fiobj_str_new("/", 1);
|
|
1177
|
-
}
|
|
1178
|
-
http_upgrade2ws(h, *s);
|
|
1179
|
-
fio_free(s);
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
static void on_websocket_http_connection_finished(http_settings_s *settings) {
|
|
1183
|
-
websocket_settings_s *s = settings->udata;
|
|
1184
|
-
if (s) {
|
|
1185
|
-
if (s->on_close)
|
|
1186
|
-
s->on_close(0, s->udata);
|
|
1187
|
-
fio_free(s);
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
#undef websocket_connect
|
|
1192
|
-
int websocket_connect(const char *address, websocket_settings_s settings) {
|
|
1193
|
-
websocket_settings_s *s = fio_malloc(sizeof(*s));
|
|
1194
|
-
FIO_ASSERT_ALLOC(s);
|
|
1195
|
-
*s = settings;
|
|
1196
|
-
return http_connect(address, NULL, .on_request = on_websocket_http_connected,
|
|
1197
|
-
.on_response = on_websocket_http_connected,
|
|
1198
|
-
.on_finish = on_websocket_http_connection_finished,
|
|
1199
|
-
.udata = s);
|
|
1200
|
-
}
|
|
1201
|
-
#define websocket_connect(address, ...) \
|
|
1202
|
-
websocket_connect((address), (websocket_settings_s){__VA_ARGS__})
|
|
1203
|
-
|
|
1204
|
-
/* *****************************************************************************
|
|
1205
|
-
EventSource Support (SSE)
|
|
1206
|
-
|
|
1207
|
-
Note:
|
|
1208
|
-
|
|
1209
|
-
* `http_sse_subscribe` and `http_sse_unsubscribe` are implemented in the
|
|
1210
|
-
http_internal logical unit.
|
|
1211
|
-
|
|
1212
|
-
***************************************************************************** */
|
|
1213
|
-
|
|
1214
|
-
static inline void http_sse_copy2str(FIOBJ dest, char *prefix, size_t pre_len,
|
|
1215
|
-
fio_str_info_s data) {
|
|
1216
|
-
if (!data.len)
|
|
1217
|
-
return;
|
|
1218
|
-
const char *stop = data.data + data.len;
|
|
1219
|
-
while (data.len) {
|
|
1220
|
-
fiobj_str_write(dest, prefix, pre_len);
|
|
1221
|
-
char *pos = data.data;
|
|
1222
|
-
while (pos < stop && *pos != '\n' && *pos != '\r')
|
|
1223
|
-
++pos;
|
|
1224
|
-
fiobj_str_write(dest, data.data, (uintptr_t)(pos - data.data));
|
|
1225
|
-
fiobj_str_write(dest, "\r\n", 2);
|
|
1226
|
-
if (*pos == '\r')
|
|
1227
|
-
++pos;
|
|
1228
|
-
if (*pos == '\n')
|
|
1229
|
-
++pos;
|
|
1230
|
-
data.len -= (uintptr_t)(pos - data.data);
|
|
1231
|
-
data.data = pos;
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
/** The on message callback. the `*msg` pointer is to a temporary object. */
|
|
1236
|
-
static void http_sse_on_message(fio_msg_s *msg) {
|
|
1237
|
-
http_sse_internal_s *sse = msg->udata1;
|
|
1238
|
-
struct http_sse_subscribe_args *args = msg->udata2;
|
|
1239
|
-
/* perform a callback */
|
|
1240
|
-
fio_protocol_s *pr = fio_protocol_try_lock(sse->uuid, FIO_PR_LOCK_TASK);
|
|
1241
|
-
if (!pr)
|
|
1242
|
-
goto postpone;
|
|
1243
|
-
args->on_message(&sse->sse, msg->channel, msg->msg, args->udata);
|
|
1244
|
-
fio_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
|
1245
|
-
return;
|
|
1246
|
-
postpone:
|
|
1247
|
-
if (errno == EBADF)
|
|
1248
|
-
return;
|
|
1249
|
-
fio_message_defer(msg);
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
static void http_sse_on_message__direct(http_sse_s *sse, fio_str_info_s channel,
|
|
1254
|
-
fio_str_info_s msg, void *udata) {
|
|
1255
|
-
http_sse_write(sse, .data = msg);
|
|
1256
|
-
(void)udata;
|
|
1257
|
-
(void)channel;
|
|
1258
|
-
}
|
|
1259
|
-
/** An optional callback for when a subscription is fully canceled. */
|
|
1260
|
-
static void http_sse_on_unsubscribe(void *sse_, void *args_) {
|
|
1261
|
-
http_sse_internal_s *sse = sse_;
|
|
1262
|
-
struct http_sse_subscribe_args *args = args_;
|
|
1263
|
-
if (args->on_unsubscribe)
|
|
1264
|
-
args->on_unsubscribe(args->udata);
|
|
1265
|
-
fio_free(args);
|
|
1266
|
-
http_sse_try_free(sse);
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
/** This macro allows easy access to the `http_sse_subscribe` function. */
|
|
1270
|
-
#undef http_sse_subscribe
|
|
1271
|
-
/**
|
|
1272
|
-
* Subscribes to a channel. See {struct http_sse_subscribe_args} for possible
|
|
1273
|
-
* arguments.
|
|
1274
|
-
*
|
|
1275
|
-
* Returns a subscription ID on success and 0 on failure.
|
|
1276
|
-
*
|
|
1277
|
-
* All subscriptions are automatically revoked once the connection is closed.
|
|
1278
|
-
*
|
|
1279
|
-
* If the connections subscribes to the same channel more than once, messages
|
|
1280
|
-
* will be merged. However, another subscription ID will be assigned, and two
|
|
1281
|
-
* calls to {http_sse_unsubscribe} will be required in order to unregister from
|
|
1282
|
-
* the channel.
|
|
1283
|
-
*/
|
|
1284
|
-
uintptr_t http_sse_subscribe(http_sse_s *sse_,
|
|
1285
|
-
struct http_sse_subscribe_args args) {
|
|
1286
|
-
http_sse_internal_s *sse = FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse_);
|
|
1287
|
-
if (sse->uuid == -1)
|
|
1288
|
-
return 0;
|
|
1289
|
-
if (!args.on_message)
|
|
1290
|
-
args.on_message = http_sse_on_message__direct;
|
|
1291
|
-
struct http_sse_subscribe_args *udata = fio_malloc(sizeof(*udata));
|
|
1292
|
-
FIO_ASSERT_ALLOC(udata);
|
|
1293
|
-
*udata = args;
|
|
1294
|
-
|
|
1295
|
-
fio_atomic_add(&sse->ref, 1);
|
|
1296
|
-
subscription_s *sub =
|
|
1297
|
-
fio_subscribe(.channel = args.channel, .on_message = http_sse_on_message,
|
|
1298
|
-
.on_unsubscribe = http_sse_on_unsubscribe, .udata1 = sse,
|
|
1299
|
-
.udata2 = udata, .match = args.match);
|
|
1300
|
-
if (!sub)
|
|
1301
|
-
return 0;
|
|
1302
|
-
|
|
1303
|
-
fio_lock(&sse->lock);
|
|
1304
|
-
fio_ls_s *pos = fio_ls_push(&sse->subscriptions, sub);
|
|
1305
|
-
fio_unlock(&sse->lock);
|
|
1306
|
-
return (uintptr_t)pos;
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
/**
|
|
1310
|
-
* Cancels a subscription and invalidates the subscription object.
|
|
1311
|
-
*/
|
|
1312
|
-
void http_sse_unsubscribe(http_sse_s *sse_, uintptr_t subscription) {
|
|
1313
|
-
if (!sse_ || !subscription)
|
|
1314
|
-
return;
|
|
1315
|
-
http_sse_internal_s *sse = FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse_);
|
|
1316
|
-
subscription_s *sub = (subscription_s *)((fio_ls_s *)subscription)->obj;
|
|
1317
|
-
fio_lock(&sse->lock);
|
|
1318
|
-
fio_ls_remove((fio_ls_s *)subscription);
|
|
1319
|
-
fio_unlock(&sse->lock);
|
|
1320
|
-
fio_unsubscribe(sub);
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
#undef http_upgrade2sse
|
|
1324
|
-
/**
|
|
1325
|
-
* Upgrades an HTTP connection to an EventSource (SSE) connection.
|
|
1326
|
-
*
|
|
1327
|
-
* Thie `http_s` handle will be invalid after this call.
|
|
1328
|
-
*
|
|
1329
|
-
* On HTTP/1.1 connections, this will preclude future requests using the same
|
|
1330
|
-
* connection.
|
|
1331
|
-
*/
|
|
1332
|
-
int http_upgrade2sse(http_s *h, http_sse_s sse) {
|
|
1333
|
-
if (HTTP_INVALID_HANDLE(h)) {
|
|
1334
|
-
if (sse.on_close)
|
|
1335
|
-
sse.on_close(&sse);
|
|
1336
|
-
return -1;
|
|
1337
|
-
}
|
|
1338
|
-
return ((http_vtable_s *)h->private_data.vtbl)->http_upgrade2sse(h, &sse);
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
/**
|
|
1342
|
-
* Sets the ping interval for SSE connections.
|
|
1343
|
-
*/
|
|
1344
|
-
void http_sse_set_timout(http_sse_s *sse_, uint8_t timeout) {
|
|
1345
|
-
if (!sse_)
|
|
1346
|
-
return;
|
|
1347
|
-
http_sse_internal_s *sse = FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse_);
|
|
1348
|
-
fio_timeout_set(sse->uuid, timeout);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
#undef http_sse_write
|
|
1352
|
-
/**
|
|
1353
|
-
* Writes data to an EventSource (SSE) connection.
|
|
1354
|
-
*/
|
|
1355
|
-
int http_sse_write(http_sse_s *sse, struct http_sse_write_args args) {
|
|
1356
|
-
if (!sse || !(args.id.len + args.data.len + args.event.len) ||
|
|
1357
|
-
fio_is_closed(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid))
|
|
1358
|
-
return -1;
|
|
1359
|
-
FIOBJ buf;
|
|
1360
|
-
{
|
|
1361
|
-
/* best guess at data length, ignoring missing fields and multiline data */
|
|
1362
|
-
const size_t total = 4 + args.id.len + 2 + 7 + args.event.len + 2 + 6 +
|
|
1363
|
-
args.data.len + 2 + 7 + 10 + 4;
|
|
1364
|
-
buf = fiobj_str_buf(total);
|
|
1365
|
-
}
|
|
1366
|
-
http_sse_copy2str(buf, (char *)"id: ", 4, args.id);
|
|
1367
|
-
http_sse_copy2str(buf, (char *)"event: ", 7, args.event);
|
|
1368
|
-
if (args.retry) {
|
|
1369
|
-
FIOBJ i = fiobj_num_new(args.retry);
|
|
1370
|
-
fiobj_str_write(buf, (char *)"retry: ", 7);
|
|
1371
|
-
fiobj_str_join(buf, i);
|
|
1372
|
-
fiobj_free(i);
|
|
1373
|
-
}
|
|
1374
|
-
http_sse_copy2str(buf, (char *)"data: ", 6, args.data);
|
|
1375
|
-
fiobj_str_write(buf, "\r\n", 2);
|
|
1376
|
-
return FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)
|
|
1377
|
-
->vtable->http_sse_write(sse, buf);
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
/**
|
|
1381
|
-
* Get the connection's UUID (for fio_defer and similar use cases).
|
|
1382
|
-
*/
|
|
1383
|
-
intptr_t http_sse2uuid(http_sse_s *sse) {
|
|
1384
|
-
if (!sse ||
|
|
1385
|
-
fio_is_closed(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid))
|
|
1386
|
-
return -1;
|
|
1387
|
-
return FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid;
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
/**
|
|
1391
|
-
* Closes an EventSource (SSE) connection.
|
|
1392
|
-
*/
|
|
1393
|
-
int http_sse_close(http_sse_s *sse) {
|
|
1394
|
-
if (!sse ||
|
|
1395
|
-
fio_is_closed(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->uuid))
|
|
1396
|
-
return -1;
|
|
1397
|
-
return FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)
|
|
1398
|
-
->vtable->http_sse_close(sse);
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
/**
|
|
1402
|
-
* Duplicates an SSE handle by reference, remember to http_sse_free.
|
|
1403
|
-
*
|
|
1404
|
-
* Returns the same object (increases a reference count, no allocation is made).
|
|
1405
|
-
*/
|
|
1406
|
-
http_sse_s *http_sse_dup(http_sse_s *sse) {
|
|
1407
|
-
fio_atomic_add(&FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse)->ref, 1);
|
|
1408
|
-
return sse;
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
/**
|
|
1412
|
-
* Frees an SSE handle by reference (decreases the reference count).
|
|
1413
|
-
*/
|
|
1414
|
-
void http_sse_free(http_sse_s *sse) {
|
|
1415
|
-
http_sse_try_free(FIO_LS_EMBD_OBJ(http_sse_internal_s, sse, sse));
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
/* *****************************************************************************
|
|
1419
|
-
HTTP GET and POST parsing helpers
|
|
1420
|
-
***************************************************************************** */
|
|
1421
|
-
|
|
1422
|
-
/** URL decodes a string, returning a `FIOBJ`. */
|
|
1423
|
-
static inline FIOBJ http_urlstr2fiobj(char *s, size_t len) {
|
|
1424
|
-
FIOBJ o = fiobj_str_buf(len);
|
|
1425
|
-
ssize_t l = http_decode_url(fiobj_obj2cstr(o).data, s, len);
|
|
1426
|
-
if (l < 0) {
|
|
1427
|
-
fiobj_free(o);
|
|
1428
|
-
return fiobj_str_new(NULL, 0); /* empty string */
|
|
1429
|
-
}
|
|
1430
|
-
fiobj_str_resize(o, (size_t)l);
|
|
1431
|
-
return o;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
/** converts a string into a `FIOBJ`. */
|
|
1435
|
-
static inline FIOBJ http_str2fiobj(char *s, size_t len, uint8_t encoded) {
|
|
1436
|
-
switch (len) {
|
|
1437
|
-
case 0:
|
|
1438
|
-
return fiobj_str_new(NULL, 0); /* empty string */
|
|
1439
|
-
case 4:
|
|
1440
|
-
if (!strncasecmp(s, "true", 4))
|
|
1441
|
-
return fiobj_true();
|
|
1442
|
-
if (!strncasecmp(s, "null", 4))
|
|
1443
|
-
return fiobj_null();
|
|
1444
|
-
break;
|
|
1445
|
-
case 5:
|
|
1446
|
-
if (!strncasecmp(s, "false", 5))
|
|
1447
|
-
return fiobj_false();
|
|
1448
|
-
}
|
|
1449
|
-
{
|
|
1450
|
-
char *end = s;
|
|
1451
|
-
const uint64_t tmp = fio_atol(&end);
|
|
1452
|
-
if (end == s + len)
|
|
1453
|
-
return fiobj_num_new(tmp);
|
|
1454
|
-
}
|
|
1455
|
-
{
|
|
1456
|
-
char *end = s;
|
|
1457
|
-
const double tmp = fio_atof(&end);
|
|
1458
|
-
if (end == s + len)
|
|
1459
|
-
return fiobj_float_new(tmp);
|
|
1460
|
-
}
|
|
1461
|
-
if (encoded)
|
|
1462
|
-
return http_urlstr2fiobj(s, len);
|
|
1463
|
-
return fiobj_str_new(s, len);
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
/** Parses the query part of an HTTP request/response. Uses `http_add2hash`. */
|
|
1467
|
-
void http_parse_query(http_s *h) {
|
|
1468
|
-
if (!h->query)
|
|
1469
|
-
return;
|
|
1470
|
-
if (!h->params)
|
|
1471
|
-
h->params = fiobj_hash_new();
|
|
1472
|
-
fio_str_info_s q = fiobj_obj2cstr(h->query);
|
|
1473
|
-
do {
|
|
1474
|
-
char *cut = memchr(q.data, '&', q.len);
|
|
1475
|
-
if (!cut)
|
|
1476
|
-
cut = q.data + q.len;
|
|
1477
|
-
char *cut2 = memchr(q.data, '=', (cut - q.data));
|
|
1478
|
-
if (cut2) {
|
|
1479
|
-
/* we only add named elements... */
|
|
1480
|
-
http_add2hash(h->params, q.data, (size_t)(cut2 - q.data), (cut2 + 1),
|
|
1481
|
-
(size_t)(cut - (cut2 + 1)), 1);
|
|
1482
|
-
}
|
|
1483
|
-
if (cut[0] == '&') {
|
|
1484
|
-
/* protecting against some ...less informed... clients */
|
|
1485
|
-
if (cut[1] == 'a' && cut[2] == 'm' && cut[3] == 'p' && cut[4] == ';')
|
|
1486
|
-
cut += 5;
|
|
1487
|
-
else
|
|
1488
|
-
cut += 1;
|
|
1489
|
-
}
|
|
1490
|
-
q.len -= (uintptr_t)(cut - q.data);
|
|
1491
|
-
q.data = cut;
|
|
1492
|
-
} while (q.len);
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
static inline void http_parse_cookies_cookie_str(FIOBJ dest, FIOBJ str,
|
|
1496
|
-
uint8_t is_url_encoded) {
|
|
1497
|
-
if (!FIOBJ_TYPE_IS(str, FIOBJ_T_STRING))
|
|
1498
|
-
return;
|
|
1499
|
-
fio_str_info_s s = fiobj_obj2cstr(str);
|
|
1500
|
-
while (s.len) {
|
|
1501
|
-
if (s.data[0] == ' ') {
|
|
1502
|
-
++s.data;
|
|
1503
|
-
--s.len;
|
|
1504
|
-
continue;
|
|
1505
|
-
}
|
|
1506
|
-
char *cut = memchr(s.data, '=', s.len);
|
|
1507
|
-
if (!cut)
|
|
1508
|
-
cut = s.data;
|
|
1509
|
-
char *cut2 = memchr(cut, ';', s.len - (cut - s.data));
|
|
1510
|
-
if (!cut2)
|
|
1511
|
-
cut2 = s.data + s.len;
|
|
1512
|
-
http_add2hash(dest, s.data, cut - s.data, cut + 1, (cut2 - (cut + 1)),
|
|
1513
|
-
is_url_encoded);
|
|
1514
|
-
if ((size_t)((cut2 + 1) - s.data) > s.len)
|
|
1515
|
-
s.len = 0;
|
|
1516
|
-
else
|
|
1517
|
-
s.len -= ((cut2 + 1) - s.data);
|
|
1518
|
-
s.data = cut2 + 1;
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
static inline void http_parse_cookies_setcookie_str(FIOBJ dest, FIOBJ str,
|
|
1522
|
-
uint8_t is_url_encoded) {
|
|
1523
|
-
if (!FIOBJ_TYPE_IS(str, FIOBJ_T_STRING))
|
|
1524
|
-
return;
|
|
1525
|
-
fio_str_info_s s = fiobj_obj2cstr(str);
|
|
1526
|
-
char *cut = memchr(s.data, '=', s.len);
|
|
1527
|
-
if (!cut)
|
|
1528
|
-
cut = s.data;
|
|
1529
|
-
char *cut2 = memchr(cut, ';', s.len - (cut - s.data));
|
|
1530
|
-
if (!cut2)
|
|
1531
|
-
cut2 = s.data + s.len;
|
|
1532
|
-
if (cut2 > cut)
|
|
1533
|
-
http_add2hash(dest, s.data, cut - s.data, cut + 1, (cut2 - (cut + 1)),
|
|
1534
|
-
is_url_encoded);
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
/** Parses any Cookie / Set-Cookie headers, using the `http_add2hash` scheme. */
|
|
1538
|
-
void http_parse_cookies(http_s *h, uint8_t is_url_encoded) {
|
|
1539
|
-
if (!h->headers)
|
|
1540
|
-
return;
|
|
1541
|
-
if (h->cookies && fiobj_hash_count(h->cookies)) {
|
|
1542
|
-
FIO_LOG_WARNING("(http) attempting to parse cookies more than once.");
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
static uint64_t setcookie_header_hash;
|
|
1546
|
-
if (!setcookie_header_hash)
|
|
1547
|
-
setcookie_header_hash = fiobj_obj2hash(HTTP_HEADER_SET_COOKIE);
|
|
1548
|
-
FIOBJ c = fiobj_hash_get2(h->headers, fiobj_obj2hash(HTTP_HEADER_COOKIE));
|
|
1549
|
-
if (c) {
|
|
1550
|
-
if (!h->cookies)
|
|
1551
|
-
h->cookies = fiobj_hash_new();
|
|
1552
|
-
if (FIOBJ_TYPE_IS(c, FIOBJ_T_ARRAY)) {
|
|
1553
|
-
/* Array of Strings */
|
|
1554
|
-
size_t count = fiobj_ary_count(c);
|
|
1555
|
-
for (size_t i = 0; i < count; ++i) {
|
|
1556
|
-
http_parse_cookies_cookie_str(
|
|
1557
|
-
h->cookies, fiobj_ary_index(c, (int64_t)i), is_url_encoded);
|
|
1558
|
-
}
|
|
1559
|
-
} else {
|
|
1560
|
-
/* single string */
|
|
1561
|
-
http_parse_cookies_cookie_str(h->cookies, c, is_url_encoded);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
c = fiobj_hash_get2(h->headers, fiobj_obj2hash(HTTP_HEADER_SET_COOKIE));
|
|
1565
|
-
if (c) {
|
|
1566
|
-
if (!h->cookies)
|
|
1567
|
-
h->cookies = fiobj_hash_new();
|
|
1568
|
-
if (FIOBJ_TYPE_IS(c, FIOBJ_T_ARRAY)) {
|
|
1569
|
-
/* Array of Strings */
|
|
1570
|
-
size_t count = fiobj_ary_count(c);
|
|
1571
|
-
for (size_t i = 0; i < count; ++i) {
|
|
1572
|
-
http_parse_cookies_setcookie_str(
|
|
1573
|
-
h->cookies, fiobj_ary_index(c, (int64_t)i), is_url_encoded);
|
|
1574
|
-
}
|
|
1575
|
-
} else {
|
|
1576
|
-
/* single string */
|
|
1577
|
-
http_parse_cookies_setcookie_str(h->cookies, c, is_url_encoded);
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
/**
|
|
1583
|
-
* Adds a named parameter to the hash, resolving nesting references.
|
|
1584
|
-
*
|
|
1585
|
-
* i.e.:
|
|
1586
|
-
*
|
|
1587
|
-
* * "name[]" references a nested Array (nested in the Hash).
|
|
1588
|
-
* * "name[key]" references a nested Hash.
|
|
1589
|
-
* * "name[][key]" references a nested Hash within an array. Hash keys will be
|
|
1590
|
-
* unique (repeating a key advances the hash).
|
|
1591
|
-
* * These rules can be nested (i.e. "name[][key1][][key2]...")
|
|
1592
|
-
* * "name[][]" is an error (there's no way for the parser to analyze
|
|
1593
|
-
* dimensions)
|
|
1594
|
-
*
|
|
1595
|
-
* Note: names can't begin with "[" or end with "]" as these are reserved
|
|
1596
|
-
* characters.
|
|
1597
|
-
*/
|
|
1598
|
-
int http_add2hash2(FIOBJ dest, char *name, size_t name_len, FIOBJ val,
|
|
1599
|
-
uint8_t encoded) {
|
|
1600
|
-
if (!name)
|
|
1601
|
-
goto error;
|
|
1602
|
-
FIOBJ nested_ary = FIOBJ_INVALID;
|
|
1603
|
-
char *cut1;
|
|
1604
|
-
/* we can't start with an empty object name */
|
|
1605
|
-
while (name_len && name[0] == '[') {
|
|
1606
|
-
--name_len;
|
|
1607
|
-
++name;
|
|
1608
|
-
}
|
|
1609
|
-
if (!name_len) {
|
|
1610
|
-
/* an empty name is an error */
|
|
1611
|
-
goto error;
|
|
1612
|
-
}
|
|
1613
|
-
uint32_t nesting = ((uint32_t)~0);
|
|
1614
|
-
rebase:
|
|
1615
|
-
/* test for nesting level limit (limit at 32) */
|
|
1616
|
-
if (!nesting)
|
|
1617
|
-
goto error;
|
|
1618
|
-
/* start clearing away bits. */
|
|
1619
|
-
nesting >>= 1;
|
|
1620
|
-
/* since we might be rebasing, notice that "name" might be "name]" */
|
|
1621
|
-
cut1 = memchr(name, '[', name_len);
|
|
1622
|
-
if (!cut1)
|
|
1623
|
-
goto place_in_hash;
|
|
1624
|
-
/* simple case "name=" (the "=" was already removed) */
|
|
1625
|
-
if (cut1 == name) {
|
|
1626
|
-
/* an empty name is an error */
|
|
1627
|
-
goto error;
|
|
1628
|
-
}
|
|
1629
|
-
if (cut1 + 1 == name + name_len) {
|
|
1630
|
-
/* we have name[= ... autocorrect */
|
|
1631
|
-
name_len -= 1;
|
|
1632
|
-
goto place_in_array;
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
if (cut1[1] == ']') {
|
|
1636
|
-
/* Nested Array "name[]..." */
|
|
1637
|
-
|
|
1638
|
-
/* Test for name[]= */
|
|
1639
|
-
if ((cut1 + 2) == name + name_len) {
|
|
1640
|
-
name_len -= 2;
|
|
1641
|
-
goto place_in_array;
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
/* Test for a nested Array format error */
|
|
1645
|
-
if (cut1[2] != '[' || cut1[3] == ']') { /* error, we can't parse this */
|
|
1646
|
-
goto error;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
/* we have name[][key...= */
|
|
1650
|
-
|
|
1651
|
-
/* ensure array exists and it's an array + set nested_ary */
|
|
1652
|
-
const size_t len = ((cut1[-1] == ']') ? (size_t)((cut1 - 1) - name)
|
|
1653
|
-
: (size_t)(cut1 - name));
|
|
1654
|
-
const uint64_t hash =
|
|
1655
|
-
fiobj_hash_string(name, len); /* hash the current name */
|
|
1656
|
-
nested_ary = fiobj_hash_get2(dest, hash);
|
|
1657
|
-
if (!nested_ary) {
|
|
1658
|
-
/* create a new nested array */
|
|
1659
|
-
FIOBJ key =
|
|
1660
|
-
encoded ? http_urlstr2fiobj(name, len) : fiobj_str_new(name, len);
|
|
1661
|
-
nested_ary = fiobj_ary_new2(4);
|
|
1662
|
-
fiobj_hash_set(dest, key, nested_ary);
|
|
1663
|
-
fiobj_free(key);
|
|
1664
|
-
} else if (!FIOBJ_TYPE_IS(nested_ary, FIOBJ_T_ARRAY)) {
|
|
1665
|
-
/* convert existing object to an array - auto error correction */
|
|
1666
|
-
FIOBJ key =
|
|
1667
|
-
encoded ? http_urlstr2fiobj(name, len) : fiobj_str_new(name, len);
|
|
1668
|
-
FIOBJ tmp = fiobj_ary_new2(4);
|
|
1669
|
-
fiobj_ary_push(tmp, nested_ary);
|
|
1670
|
-
nested_ary = tmp;
|
|
1671
|
-
fiobj_hash_set(dest, key, nested_ary);
|
|
1672
|
-
fiobj_free(key);
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
/* test if last object in the array is a hash - create hash if not */
|
|
1676
|
-
dest = fiobj_ary_index(nested_ary, -1);
|
|
1677
|
-
if (!dest || !FIOBJ_TYPE_IS(dest, FIOBJ_T_HASH)) {
|
|
1678
|
-
dest = fiobj_hash_new();
|
|
1679
|
-
fiobj_ary_push(nested_ary, dest);
|
|
1680
|
-
}
|
|
1681
|
-
|
|
1682
|
-
/* rebase `name` to `key` and restart. */
|
|
1683
|
-
cut1 += 3; /* consume "[][" */
|
|
1684
|
-
name_len -= (size_t)(cut1 - name);
|
|
1685
|
-
name = cut1;
|
|
1686
|
-
goto rebase;
|
|
1687
|
-
|
|
1688
|
-
} else {
|
|
1689
|
-
/* we have name[key]... */
|
|
1690
|
-
const size_t len = ((cut1[-1] == ']') ? (size_t)((cut1 - 1) - name)
|
|
1691
|
-
: (size_t)(cut1 - name));
|
|
1692
|
-
const uint64_t hash =
|
|
1693
|
-
fiobj_hash_string(name, len); /* hash the current name */
|
|
1694
|
-
FIOBJ tmp = fiobj_hash_get2(dest, hash);
|
|
1695
|
-
if (!tmp) {
|
|
1696
|
-
/* hash doesn't exist, create it */
|
|
1697
|
-
FIOBJ key =
|
|
1698
|
-
encoded ? http_urlstr2fiobj(name, len) : fiobj_str_new(name, len);
|
|
1699
|
-
tmp = fiobj_hash_new();
|
|
1700
|
-
fiobj_hash_set(dest, key, tmp);
|
|
1701
|
-
fiobj_free(key);
|
|
1702
|
-
} else if (!FIOBJ_TYPE_IS(tmp, FIOBJ_T_HASH)) {
|
|
1703
|
-
/* type error, referencing an existing object that isn't a Hash */
|
|
1704
|
-
goto error;
|
|
1705
|
-
}
|
|
1706
|
-
dest = tmp;
|
|
1707
|
-
/* no rollback is possible once we enter the new nesting level... */
|
|
1708
|
-
nested_ary = FIOBJ_INVALID;
|
|
1709
|
-
/* rebase `name` to `key` and restart. */
|
|
1710
|
-
cut1 += 1; /* consume "[" */
|
|
1711
|
-
name_len -= (size_t)(cut1 - name);
|
|
1712
|
-
name = cut1;
|
|
1713
|
-
goto rebase;
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
place_in_hash:
|
|
1717
|
-
if (name[name_len - 1] == ']')
|
|
1718
|
-
--name_len;
|
|
1719
|
-
{
|
|
1720
|
-
FIOBJ key = encoded ? http_urlstr2fiobj(name, name_len)
|
|
1721
|
-
: fiobj_str_new(name, name_len);
|
|
1722
|
-
FIOBJ old = fiobj_hash_replace(dest, key, val);
|
|
1723
|
-
if (old) {
|
|
1724
|
-
if (nested_ary) {
|
|
1725
|
-
fiobj_hash_replace(dest, key, old);
|
|
1726
|
-
old = fiobj_hash_new();
|
|
1727
|
-
fiobj_hash_set(old, key, val);
|
|
1728
|
-
fiobj_ary_push(nested_ary, old);
|
|
1729
|
-
} else {
|
|
1730
|
-
if (!FIOBJ_TYPE_IS(old, FIOBJ_T_ARRAY)) {
|
|
1731
|
-
FIOBJ tmp = fiobj_ary_new2(4);
|
|
1732
|
-
fiobj_ary_push(tmp, old);
|
|
1733
|
-
old = tmp;
|
|
1734
|
-
}
|
|
1735
|
-
fiobj_ary_push(old, val);
|
|
1736
|
-
fiobj_hash_replace(dest, key, old);
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
fiobj_free(key);
|
|
1740
|
-
}
|
|
1741
|
-
return 0;
|
|
1742
|
-
|
|
1743
|
-
place_in_array:
|
|
1744
|
-
if (name[name_len - 1] == ']')
|
|
1745
|
-
--name_len;
|
|
1746
|
-
{
|
|
1747
|
-
uint64_t hash = fiobj_hash_string(name, name_len);
|
|
1748
|
-
FIOBJ ary = fiobj_hash_get2(dest, hash);
|
|
1749
|
-
if (!ary) {
|
|
1750
|
-
FIOBJ key = encoded ? http_urlstr2fiobj(name, name_len)
|
|
1751
|
-
: fiobj_str_new(name, name_len);
|
|
1752
|
-
ary = fiobj_ary_new2(4);
|
|
1753
|
-
fiobj_hash_set(dest, key, ary);
|
|
1754
|
-
fiobj_free(key);
|
|
1755
|
-
} else if (!FIOBJ_TYPE_IS(ary, FIOBJ_T_ARRAY)) {
|
|
1756
|
-
FIOBJ tmp = fiobj_ary_new2(4);
|
|
1757
|
-
fiobj_ary_push(tmp, ary);
|
|
1758
|
-
ary = tmp;
|
|
1759
|
-
FIOBJ key = encoded ? http_urlstr2fiobj(name, name_len)
|
|
1760
|
-
: fiobj_str_new(name, name_len);
|
|
1761
|
-
fiobj_hash_replace(dest, key, ary);
|
|
1762
|
-
fiobj_free(key);
|
|
1763
|
-
}
|
|
1764
|
-
fiobj_ary_push(ary, val);
|
|
1765
|
-
}
|
|
1766
|
-
return 0;
|
|
1767
|
-
error:
|
|
1768
|
-
fiobj_free(val);
|
|
1769
|
-
errno = EOPNOTSUPP;
|
|
1770
|
-
return -1;
|
|
1771
|
-
}
|
|
1772
|
-
|
|
1773
|
-
/**
|
|
1774
|
-
* Adds a named parameter to the hash, resolving nesting references.
|
|
1775
|
-
*
|
|
1776
|
-
* i.e.:
|
|
1777
|
-
*
|
|
1778
|
-
* * "name[]" references a nested Array (nested in the Hash).
|
|
1779
|
-
* * "name[key]" references a nested Hash.
|
|
1780
|
-
* * "name[][key]" references a nested Hash within an array. Hash keys will be
|
|
1781
|
-
* unique (repeating a key advances the hash).
|
|
1782
|
-
* * These rules can be nested (i.e. "name[][key1][][key2]...")
|
|
1783
|
-
* * "name[][]" is an error (there's no way for the parser to analyze
|
|
1784
|
-
* dimensions)
|
|
1785
|
-
*
|
|
1786
|
-
* Note: names can't begin with "[" or end with "]" as these are reserved
|
|
1787
|
-
* characters.
|
|
1788
|
-
*/
|
|
1789
|
-
int http_add2hash(FIOBJ dest, char *name, size_t name_len, char *value,
|
|
1790
|
-
size_t value_len, uint8_t encoded) {
|
|
1791
|
-
return http_add2hash2(dest, name, name_len,
|
|
1792
|
-
http_str2fiobj(value, value_len, encoded), encoded);
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
/* *****************************************************************************
|
|
1796
|
-
HTTP Body Parsing
|
|
1797
|
-
***************************************************************************** */
|
|
1798
|
-
#include <http_mime_parser.h>
|
|
1799
|
-
|
|
1800
|
-
typedef struct {
|
|
1801
|
-
http_mime_parser_s p;
|
|
1802
|
-
http_s *h;
|
|
1803
|
-
fio_str_info_s buffer;
|
|
1804
|
-
size_t pos;
|
|
1805
|
-
size_t partial_offset;
|
|
1806
|
-
size_t partial_length;
|
|
1807
|
-
FIOBJ partial_name;
|
|
1808
|
-
} http_fio_mime_s;
|
|
1809
|
-
|
|
1810
|
-
#define http_mime_parser2fio(parser) ((http_fio_mime_s *)(parser))
|
|
1811
|
-
|
|
1812
|
-
/** Called when all the data is available at once. */
|
|
1813
|
-
static void http_mime_parser_on_data(http_mime_parser_s *parser, void *name,
|
|
1814
|
-
size_t name_len, void *filename,
|
|
1815
|
-
size_t filename_len, void *mimetype,
|
|
1816
|
-
size_t mimetype_len, void *value,
|
|
1817
|
-
size_t value_len) {
|
|
1818
|
-
if (!filename_len) {
|
|
1819
|
-
http_add2hash(http_mime_parser2fio(parser)->h->params, name, name_len,
|
|
1820
|
-
value, value_len, 0);
|
|
1821
|
-
return;
|
|
1822
|
-
}
|
|
1823
|
-
FIOBJ n = fiobj_str_new(name, name_len);
|
|
1824
|
-
fiobj_str_write(n, "[data]", 6);
|
|
1825
|
-
fio_str_info_s tmp = fiobj_obj2cstr(n);
|
|
1826
|
-
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
|
1827
|
-
value, value_len, 0);
|
|
1828
|
-
fiobj_str_resize(n, name_len);
|
|
1829
|
-
fiobj_str_write(n, "[name]", 6);
|
|
1830
|
-
tmp = fiobj_obj2cstr(n);
|
|
1831
|
-
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
|
1832
|
-
filename, filename_len, 0);
|
|
1833
|
-
if (mimetype_len) {
|
|
1834
|
-
fiobj_str_resize(n, name_len);
|
|
1835
|
-
fiobj_str_write(n, "[type]", 6);
|
|
1836
|
-
tmp = fiobj_obj2cstr(n);
|
|
1837
|
-
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
|
1838
|
-
mimetype, mimetype_len, 0);
|
|
1839
|
-
}
|
|
1840
|
-
fiobj_free(n);
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
/** Called when the data didn't fit in the buffer. Data will be streamed. */
|
|
1844
|
-
static void http_mime_parser_on_partial_start(
|
|
1845
|
-
http_mime_parser_s *parser, void *name, size_t name_len, void *filename,
|
|
1846
|
-
size_t filename_len, void *mimetype, size_t mimetype_len) {
|
|
1847
|
-
http_mime_parser2fio(parser)->partial_length = 0;
|
|
1848
|
-
http_mime_parser2fio(parser)->partial_offset = 0;
|
|
1849
|
-
http_mime_parser2fio(parser)->partial_name = fiobj_str_new(name, name_len);
|
|
1850
|
-
|
|
1851
|
-
if (!filename)
|
|
1852
|
-
return;
|
|
1853
|
-
|
|
1854
|
-
fiobj_str_write(http_mime_parser2fio(parser)->partial_name, "[type]", 6);
|
|
1855
|
-
fio_str_info_s tmp =
|
|
1856
|
-
fiobj_obj2cstr(http_mime_parser2fio(parser)->partial_name);
|
|
1857
|
-
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
|
1858
|
-
mimetype, mimetype_len, 0);
|
|
1859
|
-
|
|
1860
|
-
fiobj_str_resize(http_mime_parser2fio(parser)->partial_name, name_len);
|
|
1861
|
-
fiobj_str_write(http_mime_parser2fio(parser)->partial_name, "[name]", 6);
|
|
1862
|
-
tmp = fiobj_obj2cstr(http_mime_parser2fio(parser)->partial_name);
|
|
1863
|
-
http_add2hash(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len,
|
|
1864
|
-
filename, filename_len, 0);
|
|
1865
|
-
|
|
1866
|
-
fiobj_str_resize(http_mime_parser2fio(parser)->partial_name, name_len);
|
|
1867
|
-
fiobj_str_write(http_mime_parser2fio(parser)->partial_name, "[data]", 6);
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
/** Called when partial data is available. */
|
|
1871
|
-
static void http_mime_parser_on_partial_data(http_mime_parser_s *parser,
|
|
1872
|
-
void *value, size_t value_len) {
|
|
1873
|
-
if (!http_mime_parser2fio(parser)->partial_offset)
|
|
1874
|
-
http_mime_parser2fio(parser)->partial_offset =
|
|
1875
|
-
http_mime_parser2fio(parser)->pos +
|
|
1876
|
-
((uintptr_t)value -
|
|
1877
|
-
(uintptr_t)http_mime_parser2fio(parser)->buffer.data);
|
|
1878
|
-
http_mime_parser2fio(parser)->partial_length += value_len;
|
|
1879
|
-
(void)value;
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
/** Called when the partial data is complete. */
|
|
1883
|
-
static void http_mime_parser_on_partial_end(http_mime_parser_s *parser) {
|
|
1884
|
-
|
|
1885
|
-
fio_str_info_s tmp =
|
|
1886
|
-
fiobj_obj2cstr(http_mime_parser2fio(parser)->partial_name);
|
|
1887
|
-
FIOBJ o = FIOBJ_INVALID;
|
|
1888
|
-
if (!http_mime_parser2fio(parser)->partial_length)
|
|
1889
|
-
return;
|
|
1890
|
-
if (http_mime_parser2fio(parser)->partial_length < 42) {
|
|
1891
|
-
/* short data gets a new object */
|
|
1892
|
-
o = fiobj_str_new(http_mime_parser2fio(parser)->buffer.data +
|
|
1893
|
-
http_mime_parser2fio(parser)->partial_offset,
|
|
1894
|
-
http_mime_parser2fio(parser)->partial_length);
|
|
1895
|
-
} else {
|
|
1896
|
-
/* longer data gets a reference object (memory collision concerns) */
|
|
1897
|
-
o = fiobj_data_slice(http_mime_parser2fio(parser)->h->body,
|
|
1898
|
-
http_mime_parser2fio(parser)->partial_offset,
|
|
1899
|
-
http_mime_parser2fio(parser)->partial_length);
|
|
1900
|
-
}
|
|
1901
|
-
http_add2hash2(http_mime_parser2fio(parser)->h->params, tmp.data, tmp.len, o,
|
|
1902
|
-
0);
|
|
1903
|
-
fiobj_free(http_mime_parser2fio(parser)->partial_name);
|
|
1904
|
-
http_mime_parser2fio(parser)->partial_name = FIOBJ_INVALID;
|
|
1905
|
-
http_mime_parser2fio(parser)->partial_offset = 0;
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
/**
|
|
1909
|
-
* Called when URL decoding is required.
|
|
1910
|
-
*
|
|
1911
|
-
* Should support inplace decoding (`dest == encoded`).
|
|
1912
|
-
*
|
|
1913
|
-
* Should return the length of the decoded string.
|
|
1914
|
-
*/
|
|
1915
|
-
static inline size_t http_mime_decode_url(char *dest, const char *encoded,
|
|
1916
|
-
size_t length) {
|
|
1917
|
-
return http_decode_url(dest, encoded, length);
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
/**
|
|
1921
|
-
* Attempts to decode the request's body.
|
|
1922
|
-
*
|
|
1923
|
-
* Supported Types include:
|
|
1924
|
-
* * application/x-www-form-urlencoded
|
|
1925
|
-
* * application/json
|
|
1926
|
-
* * multipart/form-data
|
|
1927
|
-
*/
|
|
1928
|
-
int http_parse_body(http_s *h) {
|
|
1929
|
-
static uint64_t content_type_hash;
|
|
1930
|
-
if (!h->body)
|
|
1931
|
-
return -1;
|
|
1932
|
-
if (!content_type_hash)
|
|
1933
|
-
content_type_hash = fiobj_hash_string("content-type", 12);
|
|
1934
|
-
FIOBJ ct = fiobj_hash_get2(h->headers, content_type_hash);
|
|
1935
|
-
fio_str_info_s content_type = fiobj_obj2cstr(ct);
|
|
1936
|
-
if (content_type.len < 16)
|
|
1937
|
-
return -1;
|
|
1938
|
-
if (content_type.len >= 33 &&
|
|
1939
|
-
!strncasecmp("application/x-www-form-urlencoded", content_type.data,
|
|
1940
|
-
33)) {
|
|
1941
|
-
if (!h->params)
|
|
1942
|
-
h->params = fiobj_hash_new();
|
|
1943
|
-
FIOBJ tmp = h->query;
|
|
1944
|
-
h->query = h->body;
|
|
1945
|
-
http_parse_query(h);
|
|
1946
|
-
h->query = tmp;
|
|
1947
|
-
return 0;
|
|
1948
|
-
}
|
|
1949
|
-
if (content_type.len >= 16 &&
|
|
1950
|
-
!strncasecmp("application/json", content_type.data, 16)) {
|
|
1951
|
-
content_type = fiobj_obj2cstr(h->body);
|
|
1952
|
-
if (h->params)
|
|
1953
|
-
return -1;
|
|
1954
|
-
if (fiobj_json2obj(&h->params, content_type.data, content_type.len) == 0)
|
|
1955
|
-
return -1;
|
|
1956
|
-
if (FIOBJ_TYPE_IS(h->params, FIOBJ_T_HASH))
|
|
1957
|
-
return 0;
|
|
1958
|
-
FIOBJ tmp = h->params;
|
|
1959
|
-
FIOBJ key = fiobj_str_new("JSON", 4);
|
|
1960
|
-
h->params = fiobj_hash_new2(4);
|
|
1961
|
-
fiobj_hash_set(h->params, key, tmp);
|
|
1962
|
-
fiobj_free(key);
|
|
1963
|
-
return 0;
|
|
1964
|
-
}
|
|
1965
|
-
|
|
1966
|
-
http_fio_mime_s p = {.h = h};
|
|
1967
|
-
if (http_mime_parser_init(&p.p, content_type.data, content_type.len))
|
|
1968
|
-
return -1;
|
|
1969
|
-
if (!h->params)
|
|
1970
|
-
h->params = fiobj_hash_new();
|
|
1971
|
-
|
|
1972
|
-
do {
|
|
1973
|
-
size_t cons = http_mime_parse(&p.p, p.buffer.data, p.buffer.len);
|
|
1974
|
-
p.pos += cons;
|
|
1975
|
-
p.buffer = fiobj_data_pread(h->body, p.pos, 4096);
|
|
1976
|
-
} while (p.buffer.data && !p.p.done && !p.p.error);
|
|
1977
|
-
fiobj_free(p.partial_name);
|
|
1978
|
-
p.partial_name = FIOBJ_INVALID;
|
|
1979
|
-
return 0;
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
/* *****************************************************************************
|
|
1983
|
-
HTTP Helper functions that could be used globally
|
|
1984
|
-
***************************************************************************** */
|
|
1985
|
-
|
|
1986
|
-
/**
|
|
1987
|
-
* Returns a String object representing the unparsed HTTP request (HTTP
|
|
1988
|
-
* version is capped at HTTP/1.1). Mostly usable for proxy usage and
|
|
1989
|
-
* debugging.
|
|
1990
|
-
*/
|
|
1991
|
-
FIOBJ http_req2str(http_s *h) {
|
|
1992
|
-
if (HTTP_INVALID_HANDLE(h) || !fiobj_hash_count(h->headers))
|
|
1993
|
-
return FIOBJ_INVALID;
|
|
1994
|
-
|
|
1995
|
-
struct header_writer_s w;
|
|
1996
|
-
w.dest = fiobj_str_buf(0);
|
|
1997
|
-
if (h->status_str) {
|
|
1998
|
-
fiobj_str_join(w.dest, h->version);
|
|
1999
|
-
fiobj_str_write(w.dest, " ", 1);
|
|
2000
|
-
fiobj_str_join(w.dest, fiobj_num_tmp(h->status));
|
|
2001
|
-
fiobj_str_write(w.dest, " ", 1);
|
|
2002
|
-
fiobj_str_join(w.dest, h->status_str);
|
|
2003
|
-
fiobj_str_write(w.dest, "\r\n", 2);
|
|
2004
|
-
} else {
|
|
2005
|
-
fiobj_str_join(w.dest, h->method);
|
|
2006
|
-
fiobj_str_write(w.dest, " ", 1);
|
|
2007
|
-
fiobj_str_join(w.dest, h->path);
|
|
2008
|
-
if (h->query) {
|
|
2009
|
-
fiobj_str_write(w.dest, "?", 1);
|
|
2010
|
-
fiobj_str_join(w.dest, h->query);
|
|
2011
|
-
}
|
|
2012
|
-
{
|
|
2013
|
-
fio_str_info_s t = fiobj_obj2cstr(h->version);
|
|
2014
|
-
if (t.len < 6 || t.data[5] != '1')
|
|
2015
|
-
fiobj_str_write(w.dest, " HTTP/1.1\r\n", 10);
|
|
2016
|
-
else {
|
|
2017
|
-
fiobj_str_write(w.dest, " ", 1);
|
|
2018
|
-
fiobj_str_join(w.dest, h->version);
|
|
2019
|
-
fiobj_str_write(w.dest, "\r\n", 2);
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
|
|
2024
|
-
fiobj_each1(h->headers, 0, write_header, &w);
|
|
2025
|
-
fiobj_str_write(w.dest, "\r\n", 2);
|
|
2026
|
-
if (h->body) {
|
|
2027
|
-
// fiobj_data_seek(h->body, 0);
|
|
2028
|
-
// fio_str_info_s t = fiobj_data_read(h->body, 0);
|
|
2029
|
-
// fiobj_str_write(w.dest, t.data, t.len);
|
|
2030
|
-
fiobj_str_join(w.dest, h->body);
|
|
2031
|
-
}
|
|
2032
|
-
return w.dest;
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
void http_write_log(http_s *h) {
|
|
2036
|
-
FIOBJ l = fiobj_str_buf(128);
|
|
2037
|
-
|
|
2038
|
-
intptr_t bytes_sent = fiobj_obj2num(fiobj_hash_get2(
|
|
2039
|
-
h->private_data.out_headers, fiobj_obj2hash(HTTP_HEADER_CONTENT_LENGTH)));
|
|
2040
|
-
|
|
2041
|
-
struct timespec start, end;
|
|
2042
|
-
clock_gettime(CLOCK_REALTIME, &end);
|
|
2043
|
-
start = h->received_at;
|
|
2044
|
-
|
|
2045
|
-
{
|
|
2046
|
-
// TODO Guess IP address from headers (forwarded) where possible
|
|
2047
|
-
fio_str_info_s peer = fio_peer_addr(http2protocol(h)->uuid);
|
|
2048
|
-
fiobj_str_write(l, peer.data, peer.len);
|
|
2049
|
-
}
|
|
2050
|
-
fio_str_info_s buff = fiobj_obj2cstr(l);
|
|
2051
|
-
|
|
2052
|
-
if (buff.len == 0) {
|
|
2053
|
-
memcpy(buff.data, "[unknown]", 9);
|
|
2054
|
-
buff.len = 9;
|
|
2055
|
-
}
|
|
2056
|
-
memcpy(buff.data + buff.len, " - - [", 6);
|
|
2057
|
-
buff.len += 6;
|
|
2058
|
-
fiobj_str_resize(l, buff.len);
|
|
2059
|
-
{
|
|
2060
|
-
FIOBJ date;
|
|
2061
|
-
fio_lock(&date_lock);
|
|
2062
|
-
date = fiobj_dup(current_date);
|
|
2063
|
-
fio_unlock(&date_lock);
|
|
2064
|
-
fiobj_str_join(l, current_date);
|
|
2065
|
-
fiobj_free(date);
|
|
2066
|
-
}
|
|
2067
|
-
fiobj_str_write(l, "] \"", 3);
|
|
2068
|
-
fiobj_str_join(l, h->method);
|
|
2069
|
-
fiobj_str_write(l, " ", 1);
|
|
2070
|
-
fiobj_str_join(l, h->path);
|
|
2071
|
-
fiobj_str_write(l, " ", 1);
|
|
2072
|
-
fiobj_str_join(l, h->version);
|
|
2073
|
-
fiobj_str_write(l, "\" ", 2);
|
|
2074
|
-
if (bytes_sent > 0) {
|
|
2075
|
-
fiobj_str_write_i(l, h->status);
|
|
2076
|
-
fiobj_str_write(l, " ", 1);
|
|
2077
|
-
fiobj_str_write_i(l, bytes_sent);
|
|
2078
|
-
fiobj_str_write(l, "b ", 2);
|
|
2079
|
-
} else {
|
|
2080
|
-
fiobj_str_join(l, fiobj_num_tmp(h->status));
|
|
2081
|
-
fiobj_str_write(l, " -- ", 4);
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
|
-
bytes_sent = ((end.tv_sec - start.tv_sec) * 1000) +
|
|
2085
|
-
((end.tv_nsec - start.tv_nsec) / 1000000);
|
|
2086
|
-
fiobj_str_write_i(l, bytes_sent);
|
|
2087
|
-
fiobj_str_write(l, "ms\r\n", 4);
|
|
2088
|
-
|
|
2089
|
-
buff = fiobj_obj2cstr(l);
|
|
2090
|
-
fwrite(buff.data, 1, buff.len, stderr);
|
|
2091
|
-
fiobj_free(l);
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
/**
|
|
2095
|
-
A faster (yet less localized) alternative to `gmtime_r`.
|
|
2096
|
-
|
|
2097
|
-
See the libc `gmtime_r` documentation for details.
|
|
2098
|
-
|
|
2099
|
-
Falls back to `gmtime_r` for dates before epoch.
|
|
2100
|
-
*/
|
|
2101
|
-
struct tm *http_gmtime(time_t timer, struct tm *tm) {
|
|
2102
|
-
ssize_t a, b;
|
|
2103
|
-
#if HAVE_TM_TM_ZONE || defined(BSD)
|
|
2104
|
-
*tm = (struct tm){
|
|
2105
|
-
.tm_isdst = 0,
|
|
2106
|
-
.tm_zone = (char *)"UTC",
|
|
2107
|
-
};
|
|
2108
|
-
#else
|
|
2109
|
-
*tm = (struct tm){
|
|
2110
|
-
.tm_isdst = 0,
|
|
2111
|
-
};
|
|
2112
|
-
#endif
|
|
2113
|
-
|
|
2114
|
-
// convert seconds from epoch to days from epoch + extract data
|
|
2115
|
-
if (timer >= 0) {
|
|
2116
|
-
// for seconds up to weekdays, we reduce the reminder every step.
|
|
2117
|
-
a = (ssize_t)timer;
|
|
2118
|
-
b = a / 60; // b == time in minutes
|
|
2119
|
-
tm->tm_sec = a - (b * 60);
|
|
2120
|
-
a = b / 60; // b == time in hours
|
|
2121
|
-
tm->tm_min = b - (a * 60);
|
|
2122
|
-
b = a / 24; // b == time in days since epoch
|
|
2123
|
-
tm->tm_hour = a - (b * 24);
|
|
2124
|
-
// b == number of days since epoch
|
|
2125
|
-
// day of epoch was a thursday. Add + 4 so sunday == 0...
|
|
2126
|
-
tm->tm_wday = (b + 4) % 7;
|
|
2127
|
-
} else {
|
|
2128
|
-
// for seconds up to weekdays, we reduce the reminder every step.
|
|
2129
|
-
a = (ssize_t)timer;
|
|
2130
|
-
b = a / 60; // b == time in minutes
|
|
2131
|
-
if (b * 60 != a) {
|
|
2132
|
-
/* seconds passed */
|
|
2133
|
-
tm->tm_sec = (a - (b * 60)) + 60;
|
|
2134
|
-
--b;
|
|
2135
|
-
} else {
|
|
2136
|
-
/* no seconds */
|
|
2137
|
-
tm->tm_sec = 0;
|
|
2138
|
-
}
|
|
2139
|
-
a = b / 60; // b == time in hours
|
|
2140
|
-
if (a * 60 != b) {
|
|
2141
|
-
/* minutes passed */
|
|
2142
|
-
tm->tm_min = (b - (a * 60)) + 60;
|
|
2143
|
-
--a;
|
|
2144
|
-
} else {
|
|
2145
|
-
/* no minutes */
|
|
2146
|
-
tm->tm_min = 0;
|
|
2147
|
-
}
|
|
2148
|
-
b = a / 24; // b == time in days since epoch?
|
|
2149
|
-
if (b * 24 != a) {
|
|
2150
|
-
/* hours passed */
|
|
2151
|
-
tm->tm_hour = (a - (b * 24)) + 24;
|
|
2152
|
-
--b;
|
|
2153
|
-
} else {
|
|
2154
|
-
/* no hours */
|
|
2155
|
-
tm->tm_hour = 0;
|
|
2156
|
-
}
|
|
2157
|
-
// day of epoch was a thursday. Add + 4 so sunday == 0...
|
|
2158
|
-
tm->tm_wday = ((b - 3) % 7);
|
|
2159
|
-
if (tm->tm_wday)
|
|
2160
|
-
tm->tm_wday += 7;
|
|
2161
|
-
/* b == days from epoch */
|
|
2162
|
-
}
|
|
2163
|
-
|
|
2164
|
-
// at this point we can apply the algorithm described here:
|
|
2165
|
-
// http://howardhinnant.github.io/date_algorithms.html#civil_from_days
|
|
2166
|
-
// Credit to Howard Hinnant.
|
|
2167
|
-
{
|
|
2168
|
-
b += 719468L; // adjust to March 1st, 2000 (post leap of 400 year era)
|
|
2169
|
-
// 146,097 = days in era (400 years)
|
|
2170
|
-
const size_t era = (b >= 0 ? b : b - 146096) / 146097;
|
|
2171
|
-
const uint32_t doe = (b - (era * 146097)); // day of era
|
|
2172
|
-
const uint16_t yoe =
|
|
2173
|
-
(doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // year of era
|
|
2174
|
-
a = yoe;
|
|
2175
|
-
a += era * 400; // a == year number, assuming year starts on March 1st...
|
|
2176
|
-
const uint16_t doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
2177
|
-
const uint16_t mp = (5U * doy + 2) / 153;
|
|
2178
|
-
const uint16_t d = doy - (153U * mp + 2) / 5 + 1;
|
|
2179
|
-
const uint8_t m = mp + (mp < 10 ? 2 : -10);
|
|
2180
|
-
a += (m <= 1);
|
|
2181
|
-
tm->tm_year = a - 1900; // tm_year == years since 1900
|
|
2182
|
-
tm->tm_mon = m;
|
|
2183
|
-
tm->tm_mday = d;
|
|
2184
|
-
const uint8_t is_leap = (a % 4 == 0 && (a % 100 != 0 || a % 400 == 0));
|
|
2185
|
-
tm->tm_yday = (doy + (is_leap) + 28 + 31) % (365 + is_leap);
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
return tm;
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
static const char *DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed",
|
|
2192
|
-
"Thu", "Fri", "Sat"};
|
|
2193
|
-
static const char *MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ",
|
|
2194
|
-
"May ", "Jun ", "Jul ", "Aug ",
|
|
2195
|
-
"Sep ", "Oct ", "Nov ", "Dec "};
|
|
2196
|
-
static const char *GMT_STR = "GMT";
|
|
2197
|
-
|
|
2198
|
-
size_t http_date2rfc7231(char *target, struct tm *tmbuf) {
|
|
2199
|
-
/* note: day of month is always 2 digits */
|
|
2200
|
-
char *pos = target;
|
|
2201
|
-
uint16_t tmp;
|
|
2202
|
-
pos[0] = DAY_NAMES[tmbuf->tm_wday][0];
|
|
2203
|
-
pos[1] = DAY_NAMES[tmbuf->tm_wday][1];
|
|
2204
|
-
pos[2] = DAY_NAMES[tmbuf->tm_wday][2];
|
|
2205
|
-
pos[3] = ',';
|
|
2206
|
-
pos[4] = ' ';
|
|
2207
|
-
pos += 5;
|
|
2208
|
-
tmp = tmbuf->tm_mday / 10;
|
|
2209
|
-
pos[0] = '0' + tmp;
|
|
2210
|
-
pos[1] = '0' + (tmbuf->tm_mday - (tmp * 10));
|
|
2211
|
-
pos += 2;
|
|
2212
|
-
*(pos++) = ' ';
|
|
2213
|
-
pos[0] = MONTH_NAMES[tmbuf->tm_mon][0];
|
|
2214
|
-
pos[1] = MONTH_NAMES[tmbuf->tm_mon][1];
|
|
2215
|
-
pos[2] = MONTH_NAMES[tmbuf->tm_mon][2];
|
|
2216
|
-
pos[3] = ' ';
|
|
2217
|
-
pos += 4;
|
|
2218
|
-
// write year.
|
|
2219
|
-
pos += fio_ltoa(pos, tmbuf->tm_year + 1900, 10);
|
|
2220
|
-
*(pos++) = ' ';
|
|
2221
|
-
tmp = tmbuf->tm_hour / 10;
|
|
2222
|
-
pos[0] = '0' + tmp;
|
|
2223
|
-
pos[1] = '0' + (tmbuf->tm_hour - (tmp * 10));
|
|
2224
|
-
pos[2] = ':';
|
|
2225
|
-
tmp = tmbuf->tm_min / 10;
|
|
2226
|
-
pos[3] = '0' + tmp;
|
|
2227
|
-
pos[4] = '0' + (tmbuf->tm_min - (tmp * 10));
|
|
2228
|
-
pos[5] = ':';
|
|
2229
|
-
tmp = tmbuf->tm_sec / 10;
|
|
2230
|
-
pos[6] = '0' + tmp;
|
|
2231
|
-
pos[7] = '0' + (tmbuf->tm_sec - (tmp * 10));
|
|
2232
|
-
pos += 8;
|
|
2233
|
-
pos[0] = ' ';
|
|
2234
|
-
pos[1] = GMT_STR[0];
|
|
2235
|
-
pos[2] = GMT_STR[1];
|
|
2236
|
-
pos[3] = GMT_STR[2];
|
|
2237
|
-
pos[4] = 0;
|
|
2238
|
-
pos += 4;
|
|
2239
|
-
return pos - target;
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
size_t http_date2str(char *target, struct tm *tmbuf);
|
|
2243
|
-
|
|
2244
|
-
size_t http_date2rfc2822(char *target, struct tm *tmbuf) {
|
|
2245
|
-
/* note: day of month is either 1 or 2 digits */
|
|
2246
|
-
char *pos = target;
|
|
2247
|
-
uint16_t tmp;
|
|
2248
|
-
pos[0] = DAY_NAMES[tmbuf->tm_wday][0];
|
|
2249
|
-
pos[1] = DAY_NAMES[tmbuf->tm_wday][1];
|
|
2250
|
-
pos[2] = DAY_NAMES[tmbuf->tm_wday][2];
|
|
2251
|
-
pos[3] = ',';
|
|
2252
|
-
pos[4] = ' ';
|
|
2253
|
-
pos += 5;
|
|
2254
|
-
if (tmbuf->tm_mday < 10) {
|
|
2255
|
-
*pos = '0' + tmbuf->tm_mday;
|
|
2256
|
-
++pos;
|
|
2257
|
-
} else {
|
|
2258
|
-
tmp = tmbuf->tm_mday / 10;
|
|
2259
|
-
pos[0] = '0' + tmp;
|
|
2260
|
-
pos[1] = '0' + (tmbuf->tm_mday - (tmp * 10));
|
|
2261
|
-
pos += 2;
|
|
2262
|
-
}
|
|
2263
|
-
*(pos++) = '-';
|
|
2264
|
-
pos[0] = MONTH_NAMES[tmbuf->tm_mon][0];
|
|
2265
|
-
pos[1] = MONTH_NAMES[tmbuf->tm_mon][1];
|
|
2266
|
-
pos[2] = MONTH_NAMES[tmbuf->tm_mon][2];
|
|
2267
|
-
pos += 3;
|
|
2268
|
-
*(pos++) = '-';
|
|
2269
|
-
// write year.
|
|
2270
|
-
pos += fio_ltoa(pos, tmbuf->tm_year + 1900, 10);
|
|
2271
|
-
*(pos++) = ' ';
|
|
2272
|
-
tmp = tmbuf->tm_hour / 10;
|
|
2273
|
-
pos[0] = '0' + tmp;
|
|
2274
|
-
pos[1] = '0' + (tmbuf->tm_hour - (tmp * 10));
|
|
2275
|
-
pos[2] = ':';
|
|
2276
|
-
tmp = tmbuf->tm_min / 10;
|
|
2277
|
-
pos[3] = '0' + tmp;
|
|
2278
|
-
pos[4] = '0' + (tmbuf->tm_min - (tmp * 10));
|
|
2279
|
-
pos[5] = ':';
|
|
2280
|
-
tmp = tmbuf->tm_sec / 10;
|
|
2281
|
-
pos[6] = '0' + tmp;
|
|
2282
|
-
pos[7] = '0' + (tmbuf->tm_sec - (tmp * 10));
|
|
2283
|
-
pos += 8;
|
|
2284
|
-
pos[0] = ' ';
|
|
2285
|
-
pos[1] = GMT_STR[0];
|
|
2286
|
-
pos[2] = GMT_STR[1];
|
|
2287
|
-
pos[3] = GMT_STR[2];
|
|
2288
|
-
pos[4] = 0;
|
|
2289
|
-
pos += 4;
|
|
2290
|
-
return pos - target;
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
/* HTTP header format for Cookie ages */
|
|
2294
|
-
size_t http_date2rfc2109(char *target, struct tm *tmbuf) {
|
|
2295
|
-
/* note: day of month is always 2 digits */
|
|
2296
|
-
char *pos = target;
|
|
2297
|
-
uint16_t tmp;
|
|
2298
|
-
pos[0] = DAY_NAMES[tmbuf->tm_wday][0];
|
|
2299
|
-
pos[1] = DAY_NAMES[tmbuf->tm_wday][1];
|
|
2300
|
-
pos[2] = DAY_NAMES[tmbuf->tm_wday][2];
|
|
2301
|
-
pos[3] = ',';
|
|
2302
|
-
pos[4] = ' ';
|
|
2303
|
-
pos += 5;
|
|
2304
|
-
tmp = tmbuf->tm_mday / 10;
|
|
2305
|
-
pos[0] = '0' + tmp;
|
|
2306
|
-
pos[1] = '0' + (tmbuf->tm_mday - (tmp * 10));
|
|
2307
|
-
pos += 2;
|
|
2308
|
-
*(pos++) = ' ';
|
|
2309
|
-
pos[0] = MONTH_NAMES[tmbuf->tm_mon][0];
|
|
2310
|
-
pos[1] = MONTH_NAMES[tmbuf->tm_mon][1];
|
|
2311
|
-
pos[2] = MONTH_NAMES[tmbuf->tm_mon][2];
|
|
2312
|
-
pos[3] = ' ';
|
|
2313
|
-
pos += 4;
|
|
2314
|
-
// write year.
|
|
2315
|
-
pos += fio_ltoa(pos, tmbuf->tm_year + 1900, 10);
|
|
2316
|
-
*(pos++) = ' ';
|
|
2317
|
-
tmp = tmbuf->tm_hour / 10;
|
|
2318
|
-
pos[0] = '0' + tmp;
|
|
2319
|
-
pos[1] = '0' + (tmbuf->tm_hour - (tmp * 10));
|
|
2320
|
-
pos[2] = ':';
|
|
2321
|
-
tmp = tmbuf->tm_min / 10;
|
|
2322
|
-
pos[3] = '0' + tmp;
|
|
2323
|
-
pos[4] = '0' + (tmbuf->tm_min - (tmp * 10));
|
|
2324
|
-
pos[5] = ':';
|
|
2325
|
-
tmp = tmbuf->tm_sec / 10;
|
|
2326
|
-
pos[6] = '0' + tmp;
|
|
2327
|
-
pos[7] = '0' + (tmbuf->tm_sec - (tmp * 10));
|
|
2328
|
-
pos += 8;
|
|
2329
|
-
*pos++ = ' ';
|
|
2330
|
-
*pos++ = '-';
|
|
2331
|
-
*pos++ = '0';
|
|
2332
|
-
*pos++ = '0';
|
|
2333
|
-
*pos++ = '0';
|
|
2334
|
-
*pos++ = '0';
|
|
2335
|
-
*pos = 0;
|
|
2336
|
-
return pos - target;
|
|
2337
|
-
}
|
|
2338
|
-
|
|
2339
|
-
static pthread_key_t cached_tick_key;
|
|
2340
|
-
static pthread_key_t cached_httpdate_key;
|
|
2341
|
-
static pthread_key_t cached_len_key;
|
|
2342
|
-
static pthread_once_t cached_once = PTHREAD_ONCE_INIT;
|
|
2343
|
-
static void init_cached_key(void) {
|
|
2344
|
-
pthread_key_create(&cached_tick_key, free);
|
|
2345
|
-
pthread_key_create(&cached_httpdate_key, free);
|
|
2346
|
-
pthread_key_create(&cached_len_key, free);
|
|
2347
|
-
}
|
|
2348
|
-
static void init_cached_key_ptr(void) {
|
|
2349
|
-
time_t *cached_tick = malloc(sizeof(time_t));
|
|
2350
|
-
FIO_ASSERT_ALLOC(cached_tick);
|
|
2351
|
-
memset(cached_tick, 0, sizeof(time_t));
|
|
2352
|
-
char *cached_httpdate = malloc(sizeof(char) * 48);
|
|
2353
|
-
FIO_ASSERT_ALLOC(cached_tick);
|
|
2354
|
-
memset(cached_httpdate, 0, 48);
|
|
2355
|
-
size_t *cached_len = malloc(sizeof(size_t));
|
|
2356
|
-
*cached_len = 0;
|
|
2357
|
-
FIO_ASSERT_ALLOC(cached_len);
|
|
2358
|
-
pthread_setspecific(cached_tick_key, cached_tick);
|
|
2359
|
-
pthread_setspecific(cached_httpdate_key, cached_httpdate);
|
|
2360
|
-
pthread_setspecific(cached_len_key, cached_len);
|
|
2361
|
-
}
|
|
2362
|
-
|
|
2363
|
-
/**
|
|
2364
|
-
* Prints Unix time to a HTTP time formatted string.
|
|
2365
|
-
*
|
|
2366
|
-
* This variation implements cached results for faster processing, at the
|
|
2367
|
-
* price of a less accurate string.
|
|
2368
|
-
*/
|
|
2369
|
-
size_t http_time2str(char *target, const time_t t) {
|
|
2370
|
-
/* pre-print time every 1 or 2 seconds or so. */
|
|
2371
|
-
pthread_once(&cached_once, init_cached_key);
|
|
2372
|
-
char *cached_httpdate = pthread_getspecific(cached_httpdate_key);
|
|
2373
|
-
if (!cached_httpdate) {
|
|
2374
|
-
init_cached_key_ptr();
|
|
2375
|
-
cached_httpdate = pthread_getspecific(cached_httpdate_key);
|
|
2376
|
-
}
|
|
2377
|
-
time_t *cached_tick = pthread_getspecific(cached_tick_key);
|
|
2378
|
-
size_t *cached_len = pthread_getspecific(cached_len_key);
|
|
2379
|
-
time_t last_tick = fio_last_tick().tv_sec;
|
|
2380
|
-
if ((t | 7) < last_tick) {
|
|
2381
|
-
/* this is a custom time, not "now", pass through */
|
|
2382
|
-
struct tm tm;
|
|
2383
|
-
http_gmtime(t, &tm);
|
|
2384
|
-
return http_date2str(target, &tm);
|
|
2385
|
-
}
|
|
2386
|
-
if (last_tick > *cached_tick) {
|
|
2387
|
-
struct tm tm;
|
|
2388
|
-
*cached_tick = last_tick; /* refresh every second */
|
|
2389
|
-
http_gmtime(last_tick, &tm);
|
|
2390
|
-
*cached_len = http_date2str(cached_httpdate, &tm);
|
|
2391
|
-
}
|
|
2392
|
-
memcpy(target, cached_httpdate, *cached_len);
|
|
2393
|
-
return *cached_len;
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2396
|
-
/* Credit to Jonathan Leffler for the idea of a unified conditional */
|
|
2397
|
-
#define hex_val(c) \
|
|
2398
|
-
(((c) >= '0' && (c) <= '9') ? ((c)-48) \
|
|
2399
|
-
: (((c) | 32) >= 'a' && ((c) | 32) <= 'f') ? (((c) | 32) - 87) \
|
|
2400
|
-
: ({ \
|
|
2401
|
-
return -1; \
|
|
2402
|
-
0; \
|
|
2403
|
-
}))
|
|
2404
|
-
static inline int hex2byte(uint8_t *dest, const uint8_t *source) {
|
|
2405
|
-
if (source[0] >= '0' && source[0] <= '9')
|
|
2406
|
-
*dest = (source[0] - '0');
|
|
2407
|
-
else if ((source[0] | 32) >= 'a' && (source[0] | 32) <= 'f')
|
|
2408
|
-
*dest = (source[0] | 32) - ('a' - 10);
|
|
2409
|
-
else
|
|
2410
|
-
return -1;
|
|
2411
|
-
*dest <<= 4;
|
|
2412
|
-
if (source[1] >= '0' && source[1] <= '9')
|
|
2413
|
-
*dest |= (source[1] - '0');
|
|
2414
|
-
else if ((source[1] | 32) >= 'a' && (source[1] | 32) <= 'f')
|
|
2415
|
-
*dest |= (source[1] | 32) - ('a' - 10);
|
|
2416
|
-
else
|
|
2417
|
-
return -1;
|
|
2418
|
-
return 0;
|
|
2419
|
-
}
|
|
2420
|
-
|
|
2421
|
-
ssize_t http_decode_url(char *dest, const char *url_data, size_t length) {
|
|
2422
|
-
char *pos = dest;
|
|
2423
|
-
const char *end = url_data + length;
|
|
2424
|
-
while (url_data < end) {
|
|
2425
|
-
if (*url_data == '+') {
|
|
2426
|
-
// decode space
|
|
2427
|
-
*(pos++) = ' ';
|
|
2428
|
-
++url_data;
|
|
2429
|
-
} else if (*url_data == '%') {
|
|
2430
|
-
// decode hex value
|
|
2431
|
-
// this is a percent encoded value.
|
|
2432
|
-
if (hex2byte((uint8_t *)pos, (uint8_t *)&url_data[1]))
|
|
2433
|
-
return -1;
|
|
2434
|
-
pos++;
|
|
2435
|
-
url_data += 3;
|
|
2436
|
-
} else
|
|
2437
|
-
*(pos++) = *(url_data++);
|
|
2438
|
-
}
|
|
2439
|
-
*pos = 0;
|
|
2440
|
-
return pos - dest;
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
ssize_t http_decode_url_unsafe(char *dest, const char *url_data) {
|
|
2444
|
-
char *pos = dest;
|
|
2445
|
-
while (*url_data) {
|
|
2446
|
-
if (*url_data == '+') {
|
|
2447
|
-
// decode space
|
|
2448
|
-
*(pos++) = ' ';
|
|
2449
|
-
++url_data;
|
|
2450
|
-
} else if (*url_data == '%') {
|
|
2451
|
-
// decode hex value
|
|
2452
|
-
// this is a percent encoded value.
|
|
2453
|
-
if (hex2byte((uint8_t *)pos, (uint8_t *)&url_data[1]))
|
|
2454
|
-
return -1;
|
|
2455
|
-
pos++;
|
|
2456
|
-
url_data += 3;
|
|
2457
|
-
} else
|
|
2458
|
-
*(pos++) = *(url_data++);
|
|
2459
|
-
}
|
|
2460
|
-
*pos = 0;
|
|
2461
|
-
return pos - dest;
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
ssize_t http_decode_path(char *dest, const char *url_data, size_t length) {
|
|
2465
|
-
char *pos = dest;
|
|
2466
|
-
const char *end = url_data + length;
|
|
2467
|
-
while (url_data < end) {
|
|
2468
|
-
if (*url_data == '%') {
|
|
2469
|
-
// decode hex value
|
|
2470
|
-
// this is a percent encoded value.
|
|
2471
|
-
if (hex2byte((uint8_t *)pos, (uint8_t *)&url_data[1]))
|
|
2472
|
-
return -1;
|
|
2473
|
-
pos++;
|
|
2474
|
-
url_data += 3;
|
|
2475
|
-
} else
|
|
2476
|
-
*(pos++) = *(url_data++);
|
|
2477
|
-
}
|
|
2478
|
-
*pos = 0;
|
|
2479
|
-
return pos - dest;
|
|
2480
|
-
}
|
|
2481
|
-
|
|
2482
|
-
ssize_t http_decode_path_unsafe(char *dest, const char *url_data) {
|
|
2483
|
-
char *pos = dest;
|
|
2484
|
-
while (*url_data) {
|
|
2485
|
-
if (*url_data == '%') {
|
|
2486
|
-
// decode hex value
|
|
2487
|
-
// this is a percent encoded value.
|
|
2488
|
-
if (hex2byte((uint8_t *)pos, (uint8_t *)&url_data[1]))
|
|
2489
|
-
return -1;
|
|
2490
|
-
pos++;
|
|
2491
|
-
url_data += 3;
|
|
2492
|
-
} else
|
|
2493
|
-
*(pos++) = *(url_data++);
|
|
2494
|
-
}
|
|
2495
|
-
*pos = 0;
|
|
2496
|
-
return pos - dest;
|
|
2497
|
-
}
|
|
2498
|
-
|
|
2499
|
-
/* *****************************************************************************
|
|
2500
|
-
Lookup Tables / functions
|
|
2501
|
-
***************************************************************************** */
|
|
2502
|
-
|
|
2503
|
-
#define FIO_FORCE_MALLOC_TMP 1 /* use malloc for the mime registry */
|
|
2504
|
-
#define FIO_SET_NAME fio_mime_set
|
|
2505
|
-
#define FIO_SET_OBJ_TYPE FIOBJ
|
|
2506
|
-
#define FIO_SET_OBJ_COMPARE(o1, o2) (1)
|
|
2507
|
-
#define FIO_SET_OBJ_COPY(dest, o) (dest) = fiobj_dup((o))
|
|
2508
|
-
#define FIO_SET_OBJ_DESTROY(o) fiobj_free((o))
|
|
2509
|
-
|
|
2510
|
-
#include <fio.h>
|
|
2511
|
-
|
|
2512
|
-
static fio_mime_set_s fio_http_mime_types = FIO_SET_INIT;
|
|
2513
|
-
|
|
2514
|
-
#define LONGEST_FILE_EXTENSION_LENGTH 15
|
|
2515
|
-
|
|
2516
|
-
/** Registers a Mime-Type to be associated with the file extension. */
|
|
2517
|
-
void http_mimetype_register(char *file_ext, size_t file_ext_len,
|
|
2518
|
-
FIOBJ mime_type_str) {
|
|
2519
|
-
uintptr_t hash = FIO_HASH_FN(file_ext, file_ext_len, 0, 0);
|
|
2520
|
-
if (mime_type_str == FIOBJ_INVALID) {
|
|
2521
|
-
fio_mime_set_remove(&fio_http_mime_types, hash, FIOBJ_INVALID, NULL);
|
|
2522
|
-
} else {
|
|
2523
|
-
FIOBJ old = FIOBJ_INVALID;
|
|
2524
|
-
fio_mime_set_overwrite(&fio_http_mime_types, hash, mime_type_str, &old);
|
|
2525
|
-
if (old != FIOBJ_INVALID) {
|
|
2526
|
-
FIO_LOG_WARNING("mime-type collision: %.*s was %s, now %s",
|
|
2527
|
-
(int)file_ext_len, file_ext, fiobj_obj2cstr(old).data,
|
|
2528
|
-
fiobj_obj2cstr(mime_type_str).data);
|
|
2529
|
-
fiobj_free(old);
|
|
2530
|
-
}
|
|
2531
|
-
fiobj_free(mime_type_str); /* move ownership to the registry */
|
|
2532
|
-
}
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
|
-
/** Registers a Mime-Type to be associated with the file extension. */
|
|
2536
|
-
void http_mimetype_stats(void) {
|
|
2537
|
-
FIO_LOG_DEBUG("HTTP MIME hash storage count/capa: %zu / %zu",
|
|
2538
|
-
fio_mime_set_count(&fio_http_mime_types),
|
|
2539
|
-
fio_mime_set_capa(&fio_http_mime_types));
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
/**
|
|
2543
|
-
* Finds the mime-type associated with the file extension.
|
|
2544
|
-
* Remember to call `fiobj_free`.
|
|
2545
|
-
*/
|
|
2546
|
-
FIOBJ http_mimetype_find(char *file_ext, size_t file_ext_len) {
|
|
2547
|
-
uintptr_t hash = FIO_HASH_FN(file_ext, file_ext_len, 0, 0);
|
|
2548
|
-
return fiobj_dup(
|
|
2549
|
-
fio_mime_set_find(&fio_http_mime_types, hash, FIOBJ_INVALID));
|
|
2550
|
-
}
|
|
2551
|
-
|
|
2552
|
-
static pthread_key_t buffer_key;
|
|
2553
|
-
static pthread_once_t buffer_once = PTHREAD_ONCE_INIT;
|
|
2554
|
-
static void init_buffer_key(void) { pthread_key_create(&buffer_key, free); }
|
|
2555
|
-
static void init_buffer_ptr(void) {
|
|
2556
|
-
char *buffer = malloc(sizeof(char) * (LONGEST_FILE_EXTENSION_LENGTH + 1));
|
|
2557
|
-
FIO_ASSERT_ALLOC(buffer);
|
|
2558
|
-
memset(buffer, 0, sizeof(char) * (LONGEST_FILE_EXTENSION_LENGTH + 1));
|
|
2559
|
-
pthread_setspecific(buffer_key, buffer);
|
|
2560
|
-
}
|
|
2561
|
-
/**
|
|
2562
|
-
* Finds the mime-type associated with the URL.
|
|
2563
|
-
* Remember to call `fiobj_free`.
|
|
2564
|
-
*/
|
|
2565
|
-
FIOBJ http_mimetype_find2(FIOBJ url) {
|
|
2566
|
-
pthread_once(&buffer_once, init_buffer_key);
|
|
2567
|
-
char *buffer = pthread_getspecific(buffer_key);
|
|
2568
|
-
if (!buffer) {
|
|
2569
|
-
init_buffer_ptr();
|
|
2570
|
-
buffer = pthread_getspecific(buffer_key);
|
|
2571
|
-
}
|
|
2572
|
-
fio_str_info_s ext = {.data = NULL};
|
|
2573
|
-
FIOBJ mimetype;
|
|
2574
|
-
if (!url)
|
|
2575
|
-
goto finish;
|
|
2576
|
-
fio_str_info_s tmp = fiobj_obj2cstr(url);
|
|
2577
|
-
uint8_t steps = 1;
|
|
2578
|
-
while (tmp.len > steps || steps >= LONGEST_FILE_EXTENSION_LENGTH) {
|
|
2579
|
-
switch (tmp.data[tmp.len - steps]) {
|
|
2580
|
-
case '.':
|
|
2581
|
-
--steps;
|
|
2582
|
-
if (steps) {
|
|
2583
|
-
ext.len = steps;
|
|
2584
|
-
ext.data = buffer;
|
|
2585
|
-
buffer[steps] = 0;
|
|
2586
|
-
for (size_t i = 1; i <= steps; ++i) {
|
|
2587
|
-
buffer[steps - i] = tolower(tmp.data[tmp.len - i]);
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
/* fallthrough */
|
|
2591
|
-
case '/':
|
|
2592
|
-
goto finish;
|
|
2593
|
-
break;
|
|
2594
|
-
}
|
|
2595
|
-
++steps;
|
|
2596
|
-
}
|
|
2597
|
-
finish:
|
|
2598
|
-
mimetype = http_mimetype_find(ext.data, ext.len);
|
|
2599
|
-
if (!mimetype)
|
|
2600
|
-
mimetype = fiobj_dup(HTTP_HVALUE_CONTENT_TYPE_DEFAULT);
|
|
2601
|
-
return mimetype;
|
|
2602
|
-
}
|
|
2603
|
-
|
|
2604
|
-
/** Clears the Mime-Type registry (it will be empty afterthis call). */
|
|
2605
|
-
void http_mimetype_clear(void) {
|
|
2606
|
-
fio_mime_set_free(&fio_http_mime_types);
|
|
2607
|
-
fiobj_free(current_date);
|
|
2608
|
-
current_date = FIOBJ_INVALID;
|
|
2609
|
-
last_date_added = 0;
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
/**
|
|
2613
|
-
* Create with Ruby using:
|
|
2614
|
-
|
|
2615
|
-
a = []
|
|
2616
|
-
256.times {|i| a[i] = 1;}
|
|
2617
|
-
('a'.ord..'z'.ord).each {|i| a[i] = 0;}
|
|
2618
|
-
('A'.ord..'Z'.ord).each {|i| a[i] = 0;}
|
|
2619
|
-
('0'.ord..'9'.ord).each {|i| a[i] = 0;}
|
|
2620
|
-
"!#$%&'*+-.^_`|~".bytes.each {|i| a[i] = 0;}
|
|
2621
|
-
p a; nil
|
|
2622
|
-
"!#$%&'()*+-./:<=>?@[]^_`{|}~".bytes.each {|i| a[i] = 0;} # for values
|
|
2623
|
-
p a; nil
|
|
2624
|
-
*/
|
|
2625
|
-
static char invalid_cookie_name_char[256] = {
|
|
2626
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2627
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
|
|
2628
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
|
2629
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
|
|
2630
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
2631
|
-
0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2632
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2633
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2634
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2635
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2636
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
|
2637
|
-
|
|
2638
|
-
static char invalid_cookie_value_char[256] = {
|
|
2639
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2640
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
2641
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
2642
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
2643
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
2644
|
-
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2645
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2646
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2647
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2648
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2649
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
|
2650
|
-
|
|
2651
|
-
// clang-format off
|
|
2652
|
-
#define HTTP_SET_STATUS_STR(status, str) [status-100] = { .data = (char *)(#status " " str), .len = (sizeof( #status " " str) - 1) }
|
|
2653
|
-
// clang-format on
|
|
2654
|
-
|
|
2655
|
-
/** Returns the status as a C string struct */
|
|
2656
|
-
fio_str_info_s http_status2str(uintptr_t status) {
|
|
2657
|
-
static const fio_str_info_s status2str[] = {
|
|
2658
|
-
HTTP_SET_STATUS_STR(100, "Continue"),
|
|
2659
|
-
HTTP_SET_STATUS_STR(101, "Switching Protocols"),
|
|
2660
|
-
HTTP_SET_STATUS_STR(102, "Processing"),
|
|
2661
|
-
HTTP_SET_STATUS_STR(103, "Early Hints"),
|
|
2662
|
-
HTTP_SET_STATUS_STR(200, "OK"),
|
|
2663
|
-
HTTP_SET_STATUS_STR(201, "Created"),
|
|
2664
|
-
HTTP_SET_STATUS_STR(202, "Accepted"),
|
|
2665
|
-
HTTP_SET_STATUS_STR(203, "Non-Authoritative Information"),
|
|
2666
|
-
HTTP_SET_STATUS_STR(204, "No Content"),
|
|
2667
|
-
HTTP_SET_STATUS_STR(205, "Reset Content"),
|
|
2668
|
-
HTTP_SET_STATUS_STR(206, "Partial Content"),
|
|
2669
|
-
HTTP_SET_STATUS_STR(207, "Multi-Status"),
|
|
2670
|
-
HTTP_SET_STATUS_STR(208, "Already Reported"),
|
|
2671
|
-
HTTP_SET_STATUS_STR(226, "IM Used"),
|
|
2672
|
-
HTTP_SET_STATUS_STR(300, "Multiple Choices"),
|
|
2673
|
-
HTTP_SET_STATUS_STR(301, "Moved Permanently"),
|
|
2674
|
-
HTTP_SET_STATUS_STR(302, "Found"),
|
|
2675
|
-
HTTP_SET_STATUS_STR(303, "See Other"),
|
|
2676
|
-
HTTP_SET_STATUS_STR(304, "Not Modified"),
|
|
2677
|
-
HTTP_SET_STATUS_STR(305, "Use Proxy"),
|
|
2678
|
-
HTTP_SET_STATUS_STR(306, "(Unused), "),
|
|
2679
|
-
HTTP_SET_STATUS_STR(307, "Temporary Redirect"),
|
|
2680
|
-
HTTP_SET_STATUS_STR(308, "Permanent Redirect"),
|
|
2681
|
-
HTTP_SET_STATUS_STR(400, "Bad Request"),
|
|
2682
|
-
HTTP_SET_STATUS_STR(403, "Forbidden"),
|
|
2683
|
-
HTTP_SET_STATUS_STR(404, "Not Found"),
|
|
2684
|
-
HTTP_SET_STATUS_STR(401, "Unauthorized"),
|
|
2685
|
-
HTTP_SET_STATUS_STR(402, "Payment Required"),
|
|
2686
|
-
HTTP_SET_STATUS_STR(405, "Method Not Allowed"),
|
|
2687
|
-
HTTP_SET_STATUS_STR(406, "Not Acceptable"),
|
|
2688
|
-
HTTP_SET_STATUS_STR(407, "Proxy Authentication Required"),
|
|
2689
|
-
HTTP_SET_STATUS_STR(408, "Request Timeout"),
|
|
2690
|
-
HTTP_SET_STATUS_STR(409, "Conflict"),
|
|
2691
|
-
HTTP_SET_STATUS_STR(410, "Gone"),
|
|
2692
|
-
HTTP_SET_STATUS_STR(411, "Length Required"),
|
|
2693
|
-
HTTP_SET_STATUS_STR(412, "Precondition Failed"),
|
|
2694
|
-
HTTP_SET_STATUS_STR(413, "Payload Too Large"),
|
|
2695
|
-
HTTP_SET_STATUS_STR(414, "URI Too Long"),
|
|
2696
|
-
HTTP_SET_STATUS_STR(415, "Unsupported Media Type"),
|
|
2697
|
-
HTTP_SET_STATUS_STR(416, "Range Not Satisfiable"),
|
|
2698
|
-
HTTP_SET_STATUS_STR(417, "Expectation Failed"),
|
|
2699
|
-
HTTP_SET_STATUS_STR(418, "I'm a teapot"),
|
|
2700
|
-
HTTP_SET_STATUS_STR(421, "Misdirected Request"),
|
|
2701
|
-
HTTP_SET_STATUS_STR(422, "Unprocessable Entity"),
|
|
2702
|
-
HTTP_SET_STATUS_STR(423, "Locked"),
|
|
2703
|
-
HTTP_SET_STATUS_STR(424, "Failed Dependency"),
|
|
2704
|
-
HTTP_SET_STATUS_STR(425, "Unassigned"),
|
|
2705
|
-
HTTP_SET_STATUS_STR(426, "Upgrade Required"),
|
|
2706
|
-
HTTP_SET_STATUS_STR(427, "Unassigned"),
|
|
2707
|
-
HTTP_SET_STATUS_STR(428, "Precondition Required"),
|
|
2708
|
-
HTTP_SET_STATUS_STR(429, "Too Many Requests"),
|
|
2709
|
-
HTTP_SET_STATUS_STR(430, "Unassigned"),
|
|
2710
|
-
HTTP_SET_STATUS_STR(431, "Request Header Fields Too Large"),
|
|
2711
|
-
HTTP_SET_STATUS_STR(500, "Internal Server Error"),
|
|
2712
|
-
HTTP_SET_STATUS_STR(501, "Not Implemented"),
|
|
2713
|
-
HTTP_SET_STATUS_STR(502, "Bad Gateway"),
|
|
2714
|
-
HTTP_SET_STATUS_STR(503, "Service Unavailable"),
|
|
2715
|
-
HTTP_SET_STATUS_STR(504, "Gateway Timeout"),
|
|
2716
|
-
HTTP_SET_STATUS_STR(505, "HTTP Version Not Supported"),
|
|
2717
|
-
HTTP_SET_STATUS_STR(506, "Variant Also Negotiates"),
|
|
2718
|
-
HTTP_SET_STATUS_STR(507, "Insufficient Storage"),
|
|
2719
|
-
HTTP_SET_STATUS_STR(508, "Loop Detected"),
|
|
2720
|
-
HTTP_SET_STATUS_STR(509, "Unassigned"),
|
|
2721
|
-
HTTP_SET_STATUS_STR(510, "Not Extended"),
|
|
2722
|
-
HTTP_SET_STATUS_STR(511, "Network Authentication Required"),
|
|
2723
|
-
};
|
|
2724
|
-
fio_str_info_s ret = (fio_str_info_s){.len = 0, .data = NULL};
|
|
2725
|
-
if (status >= 100 &&
|
|
2726
|
-
(status - 100) < sizeof(status2str) / sizeof(status2str[0]))
|
|
2727
|
-
ret = status2str[status - 100];
|
|
2728
|
-
if (!ret.data) {
|
|
2729
|
-
ret = status2str[400];
|
|
2730
|
-
}
|
|
2731
|
-
return ret;
|
|
2732
|
-
}
|
|
2733
|
-
#undef HTTP_SET_STATUS_STR
|
|
2734
|
-
|
|
2735
|
-
#if DEBUG
|
|
2736
|
-
void http_tests(void) {
|
|
2737
|
-
fprintf(stderr, "=== Testing HTTP helpers\n");
|
|
2738
|
-
FIOBJ html_mime = http_mimetype_find("html", 4);
|
|
2739
|
-
FIO_ASSERT(html_mime,
|
|
2740
|
-
"HTML mime-type not found! Mime-Type registry invalid!\n");
|
|
2741
|
-
fiobj_free(html_mime);
|
|
2742
|
-
}
|
|
2743
|
-
#endif
|