iodine 0.2.17 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +36 -3
- data/bin/config.ru +23 -2
- data/bin/http-hello +1 -1
- data/bin/ws-shootout +5 -0
- data/ext/iodine/defer.c +468 -0
- data/ext/iodine/defer.h +105 -0
- data/ext/iodine/evio.c +263 -0
- data/ext/iodine/evio.h +133 -0
- data/ext/iodine/extconf.rb +2 -1
- data/ext/iodine/facil.c +958 -0
- data/ext/iodine/facil.h +423 -0
- data/ext/iodine/http.c +90 -0
- data/ext/iodine/http.h +50 -12
- data/ext/iodine/http1.c +200 -267
- data/ext/iodine/http1.h +17 -26
- data/ext/iodine/http1_request.c +81 -0
- data/ext/iodine/http1_request.h +58 -0
- data/ext/iodine/http1_response.c +403 -0
- data/ext/iodine/http1_response.h +90 -0
- data/ext/iodine/http1_simple_parser.c +124 -108
- data/ext/iodine/http1_simple_parser.h +8 -3
- data/ext/iodine/http_request.c +104 -0
- data/ext/iodine/http_request.h +58 -102
- data/ext/iodine/http_response.c +212 -208
- data/ext/iodine/http_response.h +89 -252
- data/ext/iodine/iodine_core.c +57 -46
- data/ext/iodine/iodine_core.h +3 -1
- data/ext/iodine/iodine_http.c +105 -81
- data/ext/iodine/iodine_websocket.c +17 -13
- data/ext/iodine/iodine_websocket.h +1 -0
- data/ext/iodine/rb-call.c +9 -7
- data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
- data/ext/iodine/rb-rack-io.c +12 -6
- data/ext/iodine/rb-rack-io.h +1 -1
- data/ext/iodine/rb-registry.c +5 -2
- data/ext/iodine/sock.c +1159 -0
- data/ext/iodine/{libsock.h → sock.h} +138 -142
- data/ext/iodine/spnlock.inc +77 -0
- data/ext/iodine/websockets.c +101 -112
- data/ext/iodine/websockets.h +38 -19
- data/iodine.gemspec +3 -3
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +6 -6
- metadata +23 -19
- data/ext/iodine/http_response_http1.h +0 -382
- data/ext/iodine/libasync.c +0 -570
- data/ext/iodine/libasync.h +0 -122
- data/ext/iodine/libreact.c +0 -350
- data/ext/iodine/libreact.h +0 -244
- data/ext/iodine/libserver.c +0 -957
- data/ext/iodine/libserver.h +0 -481
- data/ext/iodine/libsock.c +0 -1025
- data/ext/iodine/spnlock.h +0 -243
data/ext/iodine/http.h
CHANGED
@@ -6,12 +6,32 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
6
6
|
*/
|
7
7
|
#ifndef HTTP_H
|
8
8
|
#define HTTP_H
|
9
|
+
#define LIB_FACIL_HTTP_VERSION_MAJOR 0
|
10
|
+
#define LIB_FACIL_HTTP_VERSION_MINOR 4
|
11
|
+
#define LIB_FACIL_HTTP_VERSION_PATCH 0
|
12
|
+
#include "facil.h"
|
13
|
+
|
14
|
+
/** an HTTP/1.1 vs. HTTP/2 identifier. */
|
15
|
+
enum HTTP_VERSION { HTTP_V1 = 0, HTTP_V2 = 1 };
|
16
|
+
|
17
|
+
/** HTTP header information */
|
18
|
+
typedef struct {
|
19
|
+
const char *name;
|
20
|
+
union {
|
21
|
+
const char *data;
|
22
|
+
const char *value;
|
23
|
+
};
|
24
|
+
uint32_t name_len;
|
25
|
+
union {
|
26
|
+
uint32_t data_len;
|
27
|
+
uint32_t value_len;
|
28
|
+
};
|
29
|
+
} http_header_s;
|
9
30
|
|
10
31
|
/* *****************************************************************************
|
11
32
|
Core include files
|
12
33
|
*/
|
13
34
|
// clang-format off
|
14
|
-
#include "libserver.h"
|
15
35
|
#include <time.h>
|
16
36
|
#include "http_request.h"
|
17
37
|
#include "http_response.h"
|
@@ -32,7 +52,7 @@ Hard Coded Settings
|
|
32
52
|
#endif
|
33
53
|
|
34
54
|
/* *****************************************************************************
|
35
|
-
HTTP
|
55
|
+
HTTP Core API & data structure
|
36
56
|
*/
|
37
57
|
|
38
58
|
/** Manages protocol settings for the HTTP protocol */
|
@@ -63,11 +83,39 @@ typedef struct {
|
|
63
83
|
/** An HTTP connection timeout. For HTTP/1.1 this defaults to ~5 seconds.*/
|
64
84
|
uint8_t timeout;
|
65
85
|
/**
|
86
|
+
The default HTTP version which a new connection will use. At the moment, only
|
87
|
+
version HTTP/1.1 is supported.
|
88
|
+
*/
|
89
|
+
enum HTTP_VERSION version;
|
90
|
+
/**
|
66
91
|
internal flag for library use.
|
67
92
|
*/
|
68
93
|
uint8_t private_metaflags;
|
69
94
|
} http_settings_s;
|
70
95
|
|
96
|
+
typedef protocol_s *(*http_on_open_func)(intptr_t, void *);
|
97
|
+
typedef void (*http_on_finish_func)(void *);
|
98
|
+
/**
|
99
|
+
Return the callback used for creating the HTTP protocol in the `settings`.
|
100
|
+
*/
|
101
|
+
http_on_open_func http_get_on_open_func(http_settings_s *settings);
|
102
|
+
/**
|
103
|
+
Return the callback used for freeing the HTTP protocol in the `settings`.
|
104
|
+
*/
|
105
|
+
http_on_finish_func http_get_on_finish_func(http_settings_s *settings);
|
106
|
+
|
107
|
+
/**
|
108
|
+
Listens for incoming HTTP connections on the specified posrt and address,
|
109
|
+
implementing the requested settings.
|
110
|
+
|
111
|
+
Since facil.io doesn't support native TLS/SLL
|
112
|
+
*/
|
113
|
+
int http_listen(const char *port, const char *address,
|
114
|
+
http_settings_s settings);
|
115
|
+
|
116
|
+
#define http_listen(port, address, ...) \
|
117
|
+
http_listen((port), (address), (http_settings_s){__VA_ARGS__})
|
118
|
+
|
71
119
|
/* *****************************************************************************
|
72
120
|
HTTP Helper functions that might be used globally
|
73
121
|
*/
|
@@ -122,14 +170,4 @@ ssize_t http_decode_url_unsafe(char *dest, const char *url_data);
|
|
122
170
|
/** Decodes a URL encoded string. */
|
123
171
|
ssize_t http_decode_url(char *dest, const char *url_data, size_t length);
|
124
172
|
|
125
|
-
/* *****************************************************************************
|
126
|
-
HTTP versions (they depend on the settings / core data structure)
|
127
|
-
*/
|
128
|
-
|
129
|
-
#include "http1.h"
|
130
|
-
|
131
|
-
/* *****************************************************************************
|
132
|
-
HTTP listening helpers
|
133
|
-
*/
|
134
|
-
|
135
173
|
#endif
|
data/ext/iodine/http1.c
CHANGED
@@ -4,256 +4,160 @@ license: MIT
|
|
4
4
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
|
+
#include "spnlock.inc"
|
8
|
+
|
9
|
+
#include "http.h"
|
7
10
|
#include "http1.h"
|
8
|
-
#include "
|
11
|
+
#include "http1_request.h"
|
9
12
|
#include "http1_simple_parser.h"
|
13
|
+
|
10
14
|
#include <fcntl.h>
|
11
15
|
#include <stdio.h>
|
12
16
|
#include <string.h>
|
13
|
-
#include <sys/mman.h>
|
14
17
|
#include <sys/stat.h>
|
15
18
|
|
19
|
+
char *HTTP1_Protocol_String = "facil_http/1.1_protocol";
|
16
20
|
/* *****************************************************************************
|
17
|
-
HTTP/1.1 data
|
21
|
+
HTTP/1.1 data structures
|
18
22
|
*/
|
19
23
|
|
20
|
-
typedef struct {
|
24
|
+
typedef struct http1_protocol_s {
|
21
25
|
protocol_s protocol;
|
22
26
|
http_settings_s *settings;
|
23
|
-
char buffer[HTTP1_MAX_HEADER_SIZE];
|
24
|
-
size_t buffer_pos;
|
25
27
|
void (*on_request)(http_request_s *request);
|
26
|
-
|
27
|
-
|
28
|
-
} http1_protocol_s;
|
29
|
-
|
30
|
-
#define HTTP1_PROTOCOL_SIZE \
|
31
|
-
(sizeof(http1_protocol_s) + HTTP_REQUEST_SIZE(HTTP1_MAX_HEADER_COUNT))
|
32
|
-
|
33
|
-
char *HTTP1 = "http1";
|
28
|
+
struct http1_protocol_s *next;
|
29
|
+
http1_request_s request;
|
30
|
+
} http1_protocol_s;
|
34
31
|
|
35
|
-
/* *****************************************************************************
|
36
|
-
HTTP/1.1 callbacks
|
37
|
-
*/
|
38
32
|
static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol);
|
39
33
|
|
40
34
|
/* *****************************************************************************
|
41
|
-
HTTP/1.1
|
35
|
+
HTTP/1.1 pool
|
42
36
|
*/
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
struct {
|
47
|
-
void *memory;
|
48
|
-
http1_protocol_s *pool;
|
38
|
+
static struct {
|
49
39
|
spn_lock_i lock;
|
50
|
-
|
40
|
+
uint8_t init;
|
41
|
+
http1_protocol_s *next;
|
42
|
+
http1_protocol_s protocol_mem[HTTP1_POOL_SIZE];
|
43
|
+
} http1_pool = {.lock = SPN_LOCK_INIT, .init = 0};
|
51
44
|
|
52
|
-
|
53
|
-
|
45
|
+
static void http1_free(http1_protocol_s *pr) {
|
46
|
+
if ((uintptr_t)pr < (uintptr_t)http1_pool.protocol_mem ||
|
47
|
+
(uintptr_t)pr >= (uintptr_t)(http1_pool.protocol_mem + HTTP1_POOL_SIZE))
|
48
|
+
goto use_free;
|
54
49
|
spn_lock(&http1_pool.lock);
|
55
|
-
|
56
|
-
|
57
|
-
http1_pool.pool = prot->request.metadata.next;
|
50
|
+
pr->next = http1_pool.next;
|
51
|
+
http1_pool.next = pr;
|
58
52
|
spn_unlock(&http1_pool.lock);
|
59
|
-
return
|
53
|
+
return;
|
54
|
+
use_free:
|
55
|
+
free(pr);
|
56
|
+
return;
|
60
57
|
}
|
61
58
|
|
62
|
-
inline
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
spn_unlock(&http1_pool.lock);
|
59
|
+
static inline void http1_set_protocol_data(http1_protocol_s *pr) {
|
60
|
+
pr->protocol =
|
61
|
+
(protocol_s){.on_data = (void (*)(intptr_t, protocol_s *))http1_on_data,
|
62
|
+
.on_close = (void (*)(protocol_s *))http1_free};
|
63
|
+
pr->request.request = (http_request_s){.fd = 0, .http_version = HTTP_V1};
|
64
|
+
pr->request.header_pos = pr->request.buffer_pos = 0;
|
69
65
|
}
|
70
66
|
|
71
|
-
static
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
http1_pool.memory + (HTTP1_POOL_MEMORY_SIZE - HTTP1_PROTOCOL_SIZE)) {
|
95
|
-
pool_push(pos);
|
96
|
-
pos += HTTP1_PROTOCOL_SIZE;
|
97
|
-
}
|
98
|
-
}
|
99
|
-
spn_unlock(&inner_lock);
|
100
|
-
}
|
101
|
-
|
102
|
-
/** initializes the library if it wasn't already initialized. */
|
103
|
-
#define validate_mem() \
|
104
|
-
{ \
|
105
|
-
if (http1_pool.memory == NULL) \
|
106
|
-
http1_init(); \
|
107
|
-
}
|
108
|
-
|
109
|
-
inline static void http1_free(http1_protocol_s *http) {
|
110
|
-
http_request_clear(&http->request);
|
111
|
-
validate_mem();
|
112
|
-
if (((void *)http) >= http1_pool.memory &&
|
113
|
-
((void *)http) <= (http1_pool.memory + HTTP1_POOL_MEMORY_SIZE)) {
|
114
|
-
pool_push(http);
|
115
|
-
} else
|
116
|
-
free(http);
|
117
|
-
}
|
118
|
-
|
119
|
-
protocol_s *http1_alloc(intptr_t fd, http_settings_s *settings) {
|
120
|
-
validate_mem();
|
121
|
-
// HTTP/1.1 should send a busy response
|
122
|
-
// if there aren't enough available file descriptors.
|
123
|
-
if (sock_max_capacity() - sock_uuid2fd(fd) <= HTTP_BUSY_UNLESS_HAS_FDS)
|
124
|
-
goto is_busy;
|
125
|
-
// get an http object from the pool
|
126
|
-
http1_protocol_s *http = pool_pop();
|
127
|
-
// of malloc one
|
128
|
-
if (http == NULL)
|
129
|
-
http = malloc(HTTP1_PROTOCOL_SIZE);
|
130
|
-
// review allocation
|
131
|
-
if (http == NULL)
|
132
|
-
return NULL;
|
133
|
-
|
134
|
-
// we shouldn't update the `http` protocol as a struct, as this will waste
|
135
|
-
// time as the whole buffer will be zeroed out when there is no need.
|
136
|
-
|
137
|
-
// setup parsing state
|
138
|
-
http->buffer_pos = 0;
|
139
|
-
// setup protocol callbacks
|
140
|
-
http->protocol = (protocol_s){
|
141
|
-
.service = HTTP1,
|
142
|
-
.on_data = (void (*)(intptr_t, protocol_s *))http1_on_data,
|
143
|
-
.on_close = (void (*)(protocol_s *))http1_free,
|
144
|
-
};
|
145
|
-
// setup request data
|
146
|
-
http->request = (http_request_s){
|
147
|
-
.metadata.max_headers = HTTP1_MAX_HEADER_COUNT,
|
148
|
-
.metadata.fd = fd,
|
149
|
-
.metadata.owner = http,
|
150
|
-
};
|
151
|
-
// update settings
|
152
|
-
http->settings = settings;
|
153
|
-
http->on_request = settings->on_request;
|
154
|
-
// set the timeout
|
155
|
-
server_set_timeout(fd, settings->timeout);
|
156
|
-
return (protocol_s *)http;
|
157
|
-
|
158
|
-
is_busy:
|
159
|
-
if (settings->public_folder && settings->public_folder_length) {
|
160
|
-
size_t p_len = settings->public_folder_length;
|
161
|
-
struct stat file_data = {};
|
162
|
-
char fname[p_len + 8 + 1];
|
163
|
-
memcpy(fname, settings->public_folder, p_len);
|
164
|
-
if (settings->public_folder[p_len - 1] == '/' ||
|
165
|
-
settings->public_folder[p_len - 1] == '\\')
|
166
|
-
p_len--;
|
167
|
-
memcpy(fname + p_len, "/503.html", 9);
|
168
|
-
p_len += 9;
|
169
|
-
if (stat(fname, &file_data))
|
170
|
-
goto busy_no_file;
|
171
|
-
// check that we have a file and not something else
|
172
|
-
if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
|
173
|
-
goto busy_no_file;
|
174
|
-
int file = open(fname, O_RDONLY);
|
175
|
-
if (file == -1)
|
176
|
-
goto busy_no_file;
|
177
|
-
sock_packet_s *packet;
|
178
|
-
packet = sock_checkout_packet();
|
179
|
-
memcpy(packet->buffer, "HTTP/1.1 503 Service Unavailable\r\n"
|
180
|
-
"Content-Type: text/html\r\n"
|
181
|
-
"Connection: close\r\n"
|
182
|
-
"Content-Length: ",
|
183
|
-
94);
|
184
|
-
p_len = 94 + http_ul2a(packet->buffer + 94, file_data.st_size);
|
185
|
-
memcpy(packet->buffer + p_len, "\r\n\r\n", 4);
|
186
|
-
p_len += 4;
|
187
|
-
if ((off_t)(BUFFER_PACKET_SIZE - p_len) > file_data.st_size) {
|
188
|
-
if (read(file, packet->buffer + p_len, file_data.st_size) < 0) {
|
189
|
-
close(file);
|
190
|
-
sock_free_packet(packet);
|
191
|
-
goto busy_no_file;
|
192
|
-
}
|
193
|
-
close(file);
|
194
|
-
packet->length = p_len + file_data.st_size;
|
195
|
-
sock_send_packet(fd, packet);
|
196
|
-
} else {
|
197
|
-
packet->length = p_len;
|
198
|
-
sock_send_packet(fd, packet);
|
199
|
-
sock_sendfile(fd, file, 0, file_data.st_size);
|
200
|
-
}
|
201
|
-
return NULL;
|
67
|
+
static http1_protocol_s *http1_alloc(void) {
|
68
|
+
http1_protocol_s *pr;
|
69
|
+
spn_lock(&http1_pool.lock);
|
70
|
+
if (!http1_pool.next)
|
71
|
+
goto use_malloc;
|
72
|
+
pr = http1_pool.next;
|
73
|
+
http1_pool.next = pr->next;
|
74
|
+
spn_unlock(&http1_pool.lock);
|
75
|
+
http1_request_clear(&pr->request.request);
|
76
|
+
return pr;
|
77
|
+
use_malloc:
|
78
|
+
if (http1_pool.init == 0)
|
79
|
+
goto initialize;
|
80
|
+
spn_unlock(&http1_pool.lock);
|
81
|
+
// fprintf(stderr, "using malloc\n");
|
82
|
+
pr = malloc(sizeof(*pr));
|
83
|
+
http1_set_protocol_data(pr);
|
84
|
+
return pr;
|
85
|
+
initialize:
|
86
|
+
http1_pool.init = 1;
|
87
|
+
for (size_t i = 1; i < (HTTP1_POOL_SIZE - 1); i++) {
|
88
|
+
http1_set_protocol_data(http1_pool.protocol_mem + i);
|
89
|
+
http1_pool.protocol_mem[i].next = http1_pool.protocol_mem + (i + 1);
|
202
90
|
}
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
return
|
91
|
+
http1_pool.protocol_mem[HTTP1_POOL_SIZE - 1].next = NULL;
|
92
|
+
http1_pool.next = http1_pool.protocol_mem + 1;
|
93
|
+
spn_unlock(&http1_pool.lock);
|
94
|
+
http1_set_protocol_data(http1_pool.protocol_mem);
|
95
|
+
return http1_pool.protocol_mem;
|
208
96
|
}
|
209
97
|
|
210
98
|
/* *****************************************************************************
|
211
|
-
HTTP
|
99
|
+
HTTP callbacks
|
212
100
|
*/
|
213
|
-
|
214
101
|
#define HTTP_BODY_CHUNK_SIZE 3072 // 4096
|
215
102
|
|
103
|
+
static void http1_on_header_found(http_request_s *request,
|
104
|
+
http_header_s *header) {
|
105
|
+
((http1_request_s *)request)
|
106
|
+
->headers[((http1_request_s *)request)->header_pos] = *header;
|
107
|
+
((http1_request_s *)request)->header_pos += 1;
|
108
|
+
}
|
109
|
+
|
216
110
|
/* parse and call callback */
|
217
111
|
static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol) {
|
218
112
|
ssize_t len = 0;
|
219
113
|
ssize_t result;
|
220
114
|
char buff[HTTP_BODY_CHUNK_SIZE];
|
221
115
|
char *buffer;
|
222
|
-
|
116
|
+
http1_request_s *request = &protocol->request;
|
223
117
|
for (;;) {
|
224
118
|
// handle requests with no file data
|
225
|
-
if (request->body_file <= 0) {
|
119
|
+
if (request->request.body_file <= 0) {
|
226
120
|
// request headers parsing
|
227
121
|
if (len == 0) {
|
228
|
-
buffer =
|
122
|
+
buffer = request->buffer;
|
229
123
|
// make sure headers don't overflow
|
230
|
-
len = sock_read(uuid, buffer +
|
231
|
-
HTTP1_MAX_HEADER_SIZE -
|
124
|
+
len = sock_read(uuid, buffer + request->buffer_pos,
|
125
|
+
HTTP1_MAX_HEADER_SIZE - request->buffer_pos);
|
232
126
|
// update buffer read position.
|
233
|
-
|
127
|
+
request->buffer_pos += len;
|
128
|
+
// if (len > 0) {
|
129
|
+
// fprintf(stderr, "\n----\nRead from socket, %lu bytes, total
|
130
|
+
// %lu:\n",
|
131
|
+
// len, request->buffer_pos);
|
132
|
+
// for (size_t i = 0; i < request->buffer_pos; i++) {
|
133
|
+
// fprintf(stderr, "%c", buffer[i] ? buffer[i] : '-');
|
134
|
+
// }
|
135
|
+
// fprintf(stderr, "\n");
|
136
|
+
// }
|
234
137
|
}
|
235
138
|
if (len <= 0) {
|
236
139
|
return;
|
237
140
|
}
|
141
|
+
|
238
142
|
// parse headers
|
239
143
|
result =
|
240
|
-
http1_parse_request_headers(buffer,
|
144
|
+
http1_parse_request_headers(buffer, request->buffer_pos,
|
145
|
+
&request->request, http1_on_header_found);
|
241
146
|
// review result
|
242
147
|
if (result >= 0) { // headers comeplete
|
243
|
-
// mark buffer position, for HTTP pipelining
|
244
|
-
protocol->buffer_pos = result;
|
245
148
|
// are we done?
|
246
|
-
if (request->content_length == 0 || request->body_str) {
|
149
|
+
if (request->request.content_length == 0 || request->request.body_str) {
|
247
150
|
goto handle_request;
|
248
151
|
}
|
249
|
-
if (request->content_length >
|
152
|
+
if (request->request.content_length >
|
153
|
+
protocol->settings->max_body_size) {
|
250
154
|
goto body_to_big;
|
251
155
|
}
|
252
156
|
// initialize or submit body data
|
253
|
-
result =
|
254
|
-
|
157
|
+
result = http1_parse_request_body(buffer + result, len - result,
|
158
|
+
(http_request_s *)request);
|
255
159
|
if (result >= 0) {
|
256
|
-
|
160
|
+
request->buffer_pos += result;
|
257
161
|
goto handle_request;
|
258
162
|
} else if (result == -1) // parser error
|
259
163
|
goto parser_error;
|
@@ -264,17 +168,16 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol) {
|
|
264
168
|
len = 0;
|
265
169
|
continue;
|
266
170
|
}
|
267
|
-
if (request->body_file > 0) {
|
171
|
+
if (request->request.body_file > 0) {
|
172
|
+
// fprintf(stderr, "Body File\n");
|
268
173
|
parse_body:
|
269
174
|
buffer = buff;
|
270
175
|
// request body parsing
|
271
176
|
len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
|
272
177
|
if (len <= 0)
|
273
178
|
return;
|
274
|
-
result = http1_parse_request_body(buffer, len, request);
|
179
|
+
result = http1_parse_request_body(buffer, len, &request->request);
|
275
180
|
if (result >= 0) {
|
276
|
-
// set buffer pos for piplining support
|
277
|
-
protocol->buffer_pos = result;
|
278
181
|
goto handle_request;
|
279
182
|
} else if (result == -1) // parser error
|
280
183
|
goto parser_error;
|
@@ -285,121 +188,151 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol) {
|
|
285
188
|
continue;
|
286
189
|
handle_request:
|
287
190
|
// review required headers / data
|
288
|
-
if (request->host == NULL)
|
191
|
+
if (request->request.host == NULL)
|
289
192
|
goto bad_request;
|
290
193
|
http_settings_s *settings = protocol->settings;
|
291
194
|
// call request callback
|
292
195
|
if (protocol && settings &&
|
293
|
-
(settings->public_folder == NULL ||
|
294
|
-
http_response_sendfile2(
|
295
|
-
|
296
|
-
|
297
|
-
|
196
|
+
(request->request.upgrade || settings->public_folder == NULL ||
|
197
|
+
http_response_sendfile2(
|
198
|
+
NULL, &request->request, settings->public_folder,
|
199
|
+
settings->public_folder_length, request->request.path,
|
200
|
+
request->request.path_len, settings->log_static))) {
|
201
|
+
protocol->on_request(&request->request);
|
202
|
+
// fprintf(stderr, "Called on_request\n");
|
298
203
|
}
|
299
|
-
// clear request state
|
300
|
-
http_request_clear(request);
|
301
204
|
// rotate buffer for HTTP pipelining
|
302
|
-
if (
|
205
|
+
if ((ssize_t)request->buffer_pos <= result) {
|
303
206
|
len = 0;
|
207
|
+
// fprintf(stderr, "\n----\nAll data consumed.\n");
|
304
208
|
} else {
|
305
|
-
memmove(
|
306
|
-
len
|
209
|
+
memmove(request->buffer, buffer + result, request->buffer_pos - result);
|
210
|
+
len = request->buffer_pos - result;
|
211
|
+
// fprintf(stderr, "\n----\ndata after move, %lu long:\n%.*s\n", len,
|
212
|
+
// (int)len, request->buffer);
|
307
213
|
}
|
308
|
-
//
|
309
|
-
|
310
|
-
|
214
|
+
// fprintf(stderr, "data in buffer, %lu long:\n%.*s\n", len, (int)len,
|
215
|
+
// request->buffer);
|
216
|
+
// clear request state
|
217
|
+
http1_request_clear(&request->request);
|
218
|
+
request->buffer_pos = len;
|
219
|
+
// make sure to use the correct buffer.
|
220
|
+
buffer = request->buffer;
|
311
221
|
}
|
312
222
|
// no routes lead here.
|
313
223
|
fprintf(stderr,
|
314
224
|
"I am lost on a deserted island, no code can reach me here :-)\n");
|
315
225
|
return; // How did we get here?
|
316
226
|
parser_error:
|
317
|
-
if (request->headers_count
|
227
|
+
if (request->request.headers_count >= HTTP1_MAX_HEADER_COUNT)
|
318
228
|
goto too_big;
|
319
229
|
bad_request:
|
320
230
|
/* handle generally bad requests */
|
321
231
|
{
|
322
|
-
http_response_s response =
|
323
|
-
response
|
324
|
-
http_response_write_body(
|
325
|
-
http_response_finish(
|
232
|
+
http_response_s *response = http_response_create(&request->request);
|
233
|
+
response->status = 400;
|
234
|
+
http_response_write_body(response, "Bad Request", 11);
|
235
|
+
http_response_finish(response);
|
326
236
|
sock_close(uuid);
|
327
|
-
|
237
|
+
request->buffer_pos = 0;
|
328
238
|
return;
|
329
239
|
}
|
330
240
|
too_big:
|
331
241
|
/* handle oversized headers */
|
332
242
|
{
|
333
|
-
http_response_s response =
|
334
|
-
response
|
335
|
-
http_response_write_body(
|
336
|
-
http_response_finish(
|
243
|
+
http_response_s *response = http_response_create(&request->request);
|
244
|
+
response->status = 431;
|
245
|
+
http_response_write_body(response, "Request Header Fields Too Large", 31);
|
246
|
+
http_response_finish(response);
|
337
247
|
sock_close(uuid);
|
338
|
-
|
248
|
+
request->buffer_pos = 0;
|
339
249
|
return;
|
340
250
|
body_to_big:
|
341
251
|
/* handle oversized body */
|
342
252
|
{
|
343
|
-
http_response_s response =
|
344
|
-
response
|
345
|
-
http_response_write_body(
|
346
|
-
http_response_finish(
|
253
|
+
http_response_s *response = http_response_create(&request->request);
|
254
|
+
response->status = 413;
|
255
|
+
http_response_write_body(response, "Payload Too Large", 17);
|
256
|
+
http_response_finish(response);
|
347
257
|
sock_close(uuid);
|
348
|
-
|
258
|
+
request->buffer_pos = 0;
|
349
259
|
return;
|
350
260
|
}
|
351
261
|
}
|
352
262
|
}
|
353
263
|
|
354
264
|
/* *****************************************************************************
|
355
|
-
HTTP
|
265
|
+
HTTP listening helpers
|
356
266
|
*/
|
357
267
|
|
358
|
-
|
268
|
+
/**
|
269
|
+
Allocates memory for an upgradable HTTP/1.1 protocol.
|
359
270
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
if (
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
271
|
+
The protocol self destructs when the `on_close` callback is called.
|
272
|
+
*/
|
273
|
+
protocol_s *http1_on_open(intptr_t fd, http_settings_s *settings) {
|
274
|
+
if (sock_uuid2fd(fd) >= (sock_max_capacity() - HTTP_BUSY_UNLESS_HAS_FDS))
|
275
|
+
goto is_busy;
|
276
|
+
http1_protocol_s *pr = http1_alloc();
|
277
|
+
pr->request.request.fd = fd;
|
278
|
+
pr->settings = settings;
|
279
|
+
pr->on_request = settings->on_request;
|
280
|
+
facil_set_timeout(fd, pr->settings->timeout);
|
281
|
+
return (protocol_s *)pr;
|
282
|
+
|
283
|
+
is_busy:
|
284
|
+
if (settings->public_folder && settings->public_folder_length) {
|
285
|
+
size_t p_len = settings->public_folder_length;
|
286
|
+
struct stat file_data = {.st_mode = 0};
|
287
|
+
char fname[p_len + 8 + 1];
|
288
|
+
memcpy(fname, settings->public_folder, p_len);
|
289
|
+
if (settings->public_folder[p_len - 1] == '/' ||
|
290
|
+
settings->public_folder[p_len - 1] == '\\')
|
291
|
+
p_len--;
|
292
|
+
memcpy(fname + p_len, "/503.html", 9);
|
293
|
+
p_len += 9;
|
294
|
+
if (stat(fname, &file_data))
|
295
|
+
goto busy_no_file;
|
296
|
+
// check that we have a file and not something else
|
297
|
+
if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
|
298
|
+
goto busy_no_file;
|
299
|
+
int file = open(fname, O_RDONLY);
|
300
|
+
if (file == -1)
|
301
|
+
goto busy_no_file;
|
302
|
+
sock_buffer_s *buffer;
|
303
|
+
buffer = sock_buffer_checkout();
|
304
|
+
memcpy(buffer->buf,
|
305
|
+
"HTTP/1.1 503 Service Unavailable\r\n"
|
306
|
+
"Content-Type: text/html\r\n"
|
307
|
+
"Connection: close\r\n"
|
308
|
+
"Content-Length: ",
|
309
|
+
94);
|
310
|
+
p_len = 94 + http_ul2a((char *)buffer->buf + 94, file_data.st_size);
|
311
|
+
memcpy(buffer->buf + p_len, "\r\n\r\n", 4);
|
312
|
+
p_len += 4;
|
313
|
+
if ((off_t)(BUFFER_PACKET_SIZE - p_len) > file_data.st_size) {
|
314
|
+
if (read(file, buffer->buf + p_len, file_data.st_size) < 0) {
|
315
|
+
close(file);
|
316
|
+
sock_buffer_free(buffer);
|
317
|
+
goto busy_no_file;
|
318
|
+
}
|
319
|
+
close(file);
|
320
|
+
buffer->len = p_len + file_data.st_size;
|
321
|
+
sock_buffer_send(fd, buffer);
|
322
|
+
} else {
|
323
|
+
buffer->len = p_len;
|
324
|
+
sock_buffer_send(fd, buffer);
|
325
|
+
sock_sendfile(fd, file, 0, file_data.st_size);
|
326
|
+
sock_close(fd);
|
380
327
|
}
|
328
|
+
return NULL;
|
381
329
|
}
|
382
|
-
}
|
383
|
-
static void http1_on_finish(http_settings_s *settings) {
|
384
|
-
if (settings->private_metaflags & 1)
|
385
|
-
free((void *)settings->public_folder);
|
386
|
-
if (settings->private_metaflags & 2)
|
387
|
-
free(settings);
|
388
|
-
}
|
389
330
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
}
|
398
|
-
http_settings_s *settings_copy = malloc(sizeof(*settings_copy));
|
399
|
-
*settings_copy = settings;
|
400
|
-
settings_copy->private_metaflags = 2;
|
401
|
-
return server_listen(.port = port, .address = address,
|
402
|
-
.on_start = (void *)http1_on_init,
|
403
|
-
.on_finish = (void *)http1_on_finish,
|
404
|
-
.on_open = (void *)http1_alloc, .udata = settings_copy);
|
331
|
+
busy_no_file:
|
332
|
+
sock_write(fd,
|
333
|
+
"HTTP/1.1 503 Service Unavailable\r\nContent-Length: "
|
334
|
+
"13\r\n\r\nServer Busy.",
|
335
|
+
68);
|
336
|
+
sock_close(fd);
|
337
|
+
return NULL;
|
405
338
|
}
|