iodine 0.1.21 → 0.2.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/.gitignore +3 -2
- data/.travis.yml +23 -2
- data/CHANGELOG.md +9 -2
- data/README.md +232 -179
- data/Rakefile +13 -1
- data/bin/config.ru +63 -0
- data/bin/console +6 -0
- data/bin/echo +42 -32
- data/bin/http-hello +62 -0
- data/bin/http-playground +124 -0
- data/bin/playground +62 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/bin/raw-rbhttp +35 -0
- data/bin/raw_broadcast +66 -0
- data/bin/test_with_faye +40 -0
- data/bin/ws-broadcast +108 -0
- data/bin/ws-echo +108 -0
- data/exe/iodine +59 -0
- data/ext/iodine/base64.c +264 -0
- data/ext/iodine/base64.h +72 -0
- data/ext/iodine/bscrypt-common.h +109 -0
- data/ext/iodine/bscrypt.h +49 -0
- data/ext/iodine/extconf.rb +41 -0
- data/ext/iodine/hex.c +123 -0
- data/ext/iodine/hex.h +70 -0
- data/ext/iodine/http.c +200 -0
- data/ext/iodine/http.h +128 -0
- data/ext/iodine/http1.c +402 -0
- data/ext/iodine/http1.h +56 -0
- data/ext/iodine/http1_simple_parser.c +473 -0
- data/ext/iodine/http1_simple_parser.h +59 -0
- data/ext/iodine/http_request.h +128 -0
- data/ext/iodine/http_response.c +1606 -0
- data/ext/iodine/http_response.h +393 -0
- data/ext/iodine/http_response_http1.h +374 -0
- data/ext/iodine/iodine_core.c +641 -0
- data/ext/iodine/iodine_core.h +70 -0
- data/ext/iodine/iodine_http.c +615 -0
- data/ext/iodine/iodine_http.h +19 -0
- data/ext/iodine/iodine_websocket.c +430 -0
- data/ext/iodine/iodine_websocket.h +21 -0
- data/ext/iodine/libasync.c +552 -0
- data/ext/iodine/libasync.h +117 -0
- data/ext/iodine/libreact.c +347 -0
- data/ext/iodine/libreact.h +244 -0
- data/ext/iodine/libserver.c +912 -0
- data/ext/iodine/libserver.h +435 -0
- data/ext/iodine/libsock.c +950 -0
- data/ext/iodine/libsock.h +478 -0
- data/ext/iodine/misc.c +181 -0
- data/ext/iodine/misc.h +76 -0
- data/ext/iodine/random.c +193 -0
- data/ext/iodine/random.h +48 -0
- data/ext/iodine/rb-call.c +127 -0
- data/ext/iodine/rb-call.h +60 -0
- data/ext/iodine/rb-libasync.h +79 -0
- data/ext/iodine/rb-rack-io.c +389 -0
- data/ext/iodine/rb-rack-io.h +17 -0
- data/ext/iodine/rb-registry.c +213 -0
- data/ext/iodine/rb-registry.h +33 -0
- data/ext/iodine/sha1.c +359 -0
- data/ext/iodine/sha1.h +85 -0
- data/ext/iodine/sha2.c +825 -0
- data/ext/iodine/sha2.h +138 -0
- data/ext/iodine/siphash.c +136 -0
- data/ext/iodine/siphash.h +15 -0
- data/ext/iodine/spnlock.h +235 -0
- data/ext/iodine/websockets.c +696 -0
- data/ext/iodine/websockets.h +120 -0
- data/ext/iodine/xor-crypt.c +189 -0
- data/ext/iodine/xor-crypt.h +107 -0
- data/iodine.gemspec +25 -18
- data/lib/iodine.rb +57 -58
- data/lib/iodine/http.rb +0 -189
- data/lib/iodine/protocol.rb +36 -245
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +145 -2
- metadata +115 -37
- data/bin/core_http_test +0 -51
- data/bin/em playground +0 -56
- data/bin/hello_world +0 -75
- data/bin/setup +0 -7
- data/lib/iodine/client.rb +0 -5
- data/lib/iodine/core.rb +0 -102
- data/lib/iodine/core_init.rb +0 -143
- data/lib/iodine/http/hpack.rb +0 -553
- data/lib/iodine/http/http1.rb +0 -251
- data/lib/iodine/http/http2.rb +0 -507
- data/lib/iodine/http/rack_support.rb +0 -108
- data/lib/iodine/http/request.rb +0 -462
- data/lib/iodine/http/response.rb +0 -474
- data/lib/iodine/http/session.rb +0 -143
- data/lib/iodine/http/websocket_client.rb +0 -335
- data/lib/iodine/http/websocket_handler.rb +0 -101
- data/lib/iodine/http/websockets.rb +0 -336
- data/lib/iodine/io.rb +0 -56
- data/lib/iodine/logging.rb +0 -46
- data/lib/iodine/settings.rb +0 -158
- data/lib/iodine/ssl_connector.rb +0 -48
- data/lib/iodine/timers.rb +0 -95
data/ext/iodine/http.h
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
|
2
|
+
#ifndef HTTP_H
|
3
|
+
#define HTTP_H
|
4
|
+
|
5
|
+
/* *****************************************************************************
|
6
|
+
Core include files
|
7
|
+
*/
|
8
|
+
#include "libserver.h"
|
9
|
+
#include "http_request.h"
|
10
|
+
#include "http_response.h"
|
11
|
+
#include <time.h>
|
12
|
+
/* *****************************************************************************
|
13
|
+
Hard Coded Settings
|
14
|
+
*/
|
15
|
+
|
16
|
+
/** When a new connection is accepted, it will be immediately declined with a
|
17
|
+
* 503 service unavailable (server busy) response unless the following number of
|
18
|
+
* file descriptors is available.*/
|
19
|
+
#ifndef HTTP_BUSY_UNLESS_HAS_FDS
|
20
|
+
#define HTTP_BUSY_UNLESS_HAS_FDS 64
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#ifndef HTTP_DEFAULT_BODY_LIMIT
|
24
|
+
#define HTTP_DEFAULT_BODY_LIMIT (1024 * 1024 * 50)
|
25
|
+
#endif
|
26
|
+
|
27
|
+
/* *****************************************************************************
|
28
|
+
HTTP settings / core data structure
|
29
|
+
*/
|
30
|
+
|
31
|
+
/** Manages protocol settings for the HTTP protocol */
|
32
|
+
typedef struct {
|
33
|
+
/**
|
34
|
+
The maximum size of an HTTP request's body (when posting data).
|
35
|
+
|
36
|
+
Defaults to ~ 50Mb.
|
37
|
+
*/
|
38
|
+
size_t max_body_size;
|
39
|
+
/** the callback to be performed when requests come in. */
|
40
|
+
void (*on_request)(http_request_s* request);
|
41
|
+
/**
|
42
|
+
A public folder for file transfers - allows to circumvent any application
|
43
|
+
layer server and simply serve files.
|
44
|
+
*/
|
45
|
+
const char* public_folder;
|
46
|
+
/**
|
47
|
+
The length of the public_folder string.
|
48
|
+
*/
|
49
|
+
size_t public_folder_length;
|
50
|
+
/**
|
51
|
+
Logging flag - set to TRUE to log static file requests.
|
52
|
+
|
53
|
+
Dynamic request logging is always the dynamic application's responsibility.
|
54
|
+
*/
|
55
|
+
uint8_t log_static;
|
56
|
+
/** An HTTP connection timeout. For HTTP/1.1 this defaults to ~5 seconds.*/
|
57
|
+
uint8_t timeout;
|
58
|
+
/**
|
59
|
+
internal flag for library use.
|
60
|
+
*/
|
61
|
+
uint8_t private_metaflags;
|
62
|
+
} http_settings_s;
|
63
|
+
|
64
|
+
/* *****************************************************************************
|
65
|
+
HTTP Helper functions that might be used globally
|
66
|
+
*/
|
67
|
+
|
68
|
+
/**
|
69
|
+
A faster (yet less localized) alternative to `gmtime_r`.
|
70
|
+
|
71
|
+
See the libc `gmtime_r` documentation for details.
|
72
|
+
|
73
|
+
Falls back to `gmtime_r` for dates before epoch.
|
74
|
+
*/
|
75
|
+
struct tm* http_gmtime(const time_t* timer, struct tm* tmbuf);
|
76
|
+
|
77
|
+
/**
|
78
|
+
Writes an HTTP date string to the `target` buffer.
|
79
|
+
|
80
|
+
This requires _____ bytes of space to be available at the target buffer.
|
81
|
+
|
82
|
+
Returns the number of bytes actually written.
|
83
|
+
*/
|
84
|
+
size_t http_date2str(char* target, struct tm* tmbuf);
|
85
|
+
|
86
|
+
/**
|
87
|
+
A fast, inline alternative to `sprintf(dest, "%lu", num)`.
|
88
|
+
|
89
|
+
Writes an **unsigned** number to a buffer, as a string. This is an unsafe
|
90
|
+
functions that assumes the buffer will have at least 21 bytes and isn't NULL.
|
91
|
+
|
92
|
+
A NULL terminating byte is written.
|
93
|
+
|
94
|
+
Returns the number of bytes actually written (excluding the NULL byte).
|
95
|
+
*/
|
96
|
+
inline size_t http_ul2a(char* dest, size_t num) {
|
97
|
+
uint8_t digits = 1;
|
98
|
+
size_t tmp = num;
|
99
|
+
while ((tmp /= 10))
|
100
|
+
++digits;
|
101
|
+
|
102
|
+
dest += digits;
|
103
|
+
*(dest--) = 0;
|
104
|
+
for (size_t i = 0; i < digits; i++) {
|
105
|
+
num = num - (10 * (tmp = (num / 10)));
|
106
|
+
*(dest--) = '0' + num;
|
107
|
+
num = tmp;
|
108
|
+
}
|
109
|
+
return digits;
|
110
|
+
}
|
111
|
+
|
112
|
+
/** Decodes a URL encoded string, no buffer overflow protection. */
|
113
|
+
ssize_t http_decode_url_unsafe(char* dest, const char* url_data);
|
114
|
+
|
115
|
+
/** Decodes a URL encoded string. */
|
116
|
+
ssize_t http_decode_url(char* dest, const char* url_data, size_t length);
|
117
|
+
|
118
|
+
/* *****************************************************************************
|
119
|
+
HTTP versions (they depend on the settings / core data structure)
|
120
|
+
*/
|
121
|
+
|
122
|
+
#include "http1.h"
|
123
|
+
|
124
|
+
/* *****************************************************************************
|
125
|
+
HTTP listening helpers
|
126
|
+
*/
|
127
|
+
|
128
|
+
#endif
|
data/ext/iodine/http1.c
ADDED
@@ -0,0 +1,402 @@
|
|
1
|
+
#include "http1.h"
|
2
|
+
#include "http1_simple_parser.h"
|
3
|
+
#include <string.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <fcntl.h>
|
6
|
+
#include <sys/stat.h>
|
7
|
+
#include "errno.h"
|
8
|
+
#include <sys/mman.h>
|
9
|
+
|
10
|
+
/* *****************************************************************************
|
11
|
+
HTTP/1.1 data structure
|
12
|
+
*/
|
13
|
+
|
14
|
+
typedef struct {
|
15
|
+
protocol_s protocol;
|
16
|
+
http_settings_s* settings;
|
17
|
+
char buffer[HTTP1_MAX_HEADER_SIZE];
|
18
|
+
size_t buffer_pos;
|
19
|
+
void (*on_request)(http_request_s* request);
|
20
|
+
http_request_s
|
21
|
+
request; /* MUST be last, as it has memory extensions for the headers*/
|
22
|
+
} http1_protocol_s; /* ~ 8416 bytes for (8 * 1024) buffer size and 64 headers */
|
23
|
+
|
24
|
+
#define HTTP1_PROTOCOL_SIZE \
|
25
|
+
(sizeof(http1_protocol_s) + HTTP_REQUEST_SIZE(HTTP1_MAX_HEADER_COUNT))
|
26
|
+
|
27
|
+
char* HTTP1 = "http1";
|
28
|
+
|
29
|
+
/* *****************************************************************************
|
30
|
+
HTTP/1.1 callbacks
|
31
|
+
*/
|
32
|
+
static void http1_on_data(intptr_t uuid, http1_protocol_s* protocol);
|
33
|
+
|
34
|
+
/* *****************************************************************************
|
35
|
+
HTTP/1.1 protocol pooling
|
36
|
+
*/
|
37
|
+
|
38
|
+
#define HTTP1_POOL_MEMORY_SIZE (HTTP1_PROTOCOL_SIZE * HTTP1_POOL_SIZE)
|
39
|
+
|
40
|
+
struct {
|
41
|
+
void* memory;
|
42
|
+
http1_protocol_s* pool;
|
43
|
+
spn_lock_i lock;
|
44
|
+
} http1_pool = {0};
|
45
|
+
|
46
|
+
inline static http1_protocol_s* pool_pop() {
|
47
|
+
http1_protocol_s* prot;
|
48
|
+
spn_lock(&http1_pool.lock);
|
49
|
+
prot = http1_pool.pool;
|
50
|
+
if (prot)
|
51
|
+
http1_pool.pool = prot->request.metadata.next;
|
52
|
+
spn_unlock(&http1_pool.lock);
|
53
|
+
return prot;
|
54
|
+
}
|
55
|
+
|
56
|
+
inline static void pool_push(http1_protocol_s* protocol) {
|
57
|
+
if (protocol == NULL)
|
58
|
+
return;
|
59
|
+
spn_lock(&http1_pool.lock);
|
60
|
+
protocol->request.metadata.next = http1_pool.pool;
|
61
|
+
http1_pool.pool = protocol;
|
62
|
+
spn_unlock(&http1_pool.lock);
|
63
|
+
}
|
64
|
+
|
65
|
+
static void http1_cleanup(void) {
|
66
|
+
// free memory
|
67
|
+
if (http1_pool.memory) {
|
68
|
+
munmap(http1_pool.memory, HTTP1_POOL_MEMORY_SIZE);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
static void http1_init(void) {
|
73
|
+
static spn_lock_i inner_lock = SPN_LOCK_INIT;
|
74
|
+
spn_lock(&inner_lock);
|
75
|
+
if (http1_pool.memory == NULL) {
|
76
|
+
// Allocate the memory
|
77
|
+
http1_pool.memory =
|
78
|
+
mmap(NULL, HTTP1_POOL_MEMORY_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
|
79
|
+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
80
|
+
if (http1_pool.memory == NULL)
|
81
|
+
return;
|
82
|
+
// setup `atexit` cleanup rutine
|
83
|
+
atexit(http1_cleanup);
|
84
|
+
|
85
|
+
// initialize pool
|
86
|
+
void* pos = http1_pool.memory;
|
87
|
+
while (pos <
|
88
|
+
http1_pool.memory + (HTTP1_POOL_MEMORY_SIZE - HTTP1_PROTOCOL_SIZE)) {
|
89
|
+
pool_push(pos);
|
90
|
+
pos += HTTP1_PROTOCOL_SIZE;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
spn_unlock(&inner_lock);
|
94
|
+
}
|
95
|
+
|
96
|
+
/** initializes the library if it wasn't already initialized. */
|
97
|
+
#define validate_mem() \
|
98
|
+
{ \
|
99
|
+
if (http1_pool.memory == NULL) \
|
100
|
+
http1_init(); \
|
101
|
+
}
|
102
|
+
|
103
|
+
inline static void http1_free(http1_protocol_s* http) {
|
104
|
+
http_request_clear(&http->request);
|
105
|
+
validate_mem();
|
106
|
+
if (((void*)http) >= http1_pool.memory &&
|
107
|
+
((void*)http) <= (http1_pool.memory + HTTP1_POOL_MEMORY_SIZE)) {
|
108
|
+
pool_push(http);
|
109
|
+
} else
|
110
|
+
free(http);
|
111
|
+
}
|
112
|
+
|
113
|
+
protocol_s* http1_alloc(intptr_t fd, http_settings_s* settings) {
|
114
|
+
validate_mem();
|
115
|
+
// HTTP/1.1 should send a busy response
|
116
|
+
// if there aren't enough available file descriptors.
|
117
|
+
if (sock_max_capacity() - sock_uuid2fd(fd) <= HTTP_BUSY_UNLESS_HAS_FDS)
|
118
|
+
goto is_busy;
|
119
|
+
// get an http object from the pool
|
120
|
+
http1_protocol_s* http = pool_pop();
|
121
|
+
// of malloc one
|
122
|
+
if (http == NULL)
|
123
|
+
http = malloc(HTTP1_PROTOCOL_SIZE);
|
124
|
+
// review allocation
|
125
|
+
if (http == NULL)
|
126
|
+
return NULL;
|
127
|
+
|
128
|
+
// we shouldn't update the `http` protocol as a struct, as this will waste
|
129
|
+
// time as the whole buffer will be zeroed out when there is no need.
|
130
|
+
|
131
|
+
// setup parsing state
|
132
|
+
http->buffer_pos = 0;
|
133
|
+
// setup protocol callbacks
|
134
|
+
http->protocol = (protocol_s){
|
135
|
+
.service = HTTP1,
|
136
|
+
.on_data = (void (*)(intptr_t, protocol_s*))http1_on_data,
|
137
|
+
.on_close = (void (*)(protocol_s*))http1_free,
|
138
|
+
};
|
139
|
+
// setup request data
|
140
|
+
http->request = (http_request_s){
|
141
|
+
.metadata.max_headers = HTTP1_MAX_HEADER_COUNT,
|
142
|
+
.metadata.fd = fd,
|
143
|
+
.metadata.owner = http,
|
144
|
+
};
|
145
|
+
// update settings
|
146
|
+
http->settings = settings;
|
147
|
+
http->on_request = settings->on_request;
|
148
|
+
// set the timeout
|
149
|
+
server_set_timeout(fd, settings->timeout);
|
150
|
+
return (protocol_s*)http;
|
151
|
+
|
152
|
+
is_busy:
|
153
|
+
if (settings->public_folder && settings->public_folder_length) {
|
154
|
+
size_t p_len = settings->public_folder_length;
|
155
|
+
struct stat file_data = {};
|
156
|
+
char fname[p_len + 8 + 1];
|
157
|
+
memcpy(fname, settings->public_folder, p_len);
|
158
|
+
if (settings->public_folder[p_len - 1] == '/' ||
|
159
|
+
settings->public_folder[p_len - 1] == '\\')
|
160
|
+
p_len--;
|
161
|
+
memcpy(fname + p_len, "/503.html", 9);
|
162
|
+
p_len += 9;
|
163
|
+
if (stat(fname, &file_data))
|
164
|
+
goto busy_no_file;
|
165
|
+
// check that we have a file and not something else
|
166
|
+
if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
|
167
|
+
goto busy_no_file;
|
168
|
+
int file = open(fname, O_RDONLY);
|
169
|
+
if (file == -1)
|
170
|
+
goto busy_no_file;
|
171
|
+
sock_packet_s* packet;
|
172
|
+
packet = sock_checkout_packet();
|
173
|
+
memcpy(packet->buffer,
|
174
|
+
"HTTP/1.1 503 Service Unavailable\r\n"
|
175
|
+
"Content-Type: text/html\r\n"
|
176
|
+
"Connection: close\r\n"
|
177
|
+
"Content-Length: ",
|
178
|
+
94);
|
179
|
+
p_len = 94 + http_ul2a(packet->buffer + 94, file_data.st_size);
|
180
|
+
memcpy(packet->buffer + p_len, "\r\n\r\n", 4);
|
181
|
+
p_len += 4;
|
182
|
+
if (BUFFER_PACKET_SIZE - p_len > file_data.st_size) {
|
183
|
+
if (read(file, packet->buffer + p_len, file_data.st_size) < 0) {
|
184
|
+
close(file);
|
185
|
+
sock_free_packet(packet);
|
186
|
+
goto busy_no_file;
|
187
|
+
}
|
188
|
+
close(file);
|
189
|
+
packet->length = p_len + file_data.st_size;
|
190
|
+
sock_send_packet(fd, packet);
|
191
|
+
} else {
|
192
|
+
packet->length = p_len;
|
193
|
+
sock_send_packet(fd, packet);
|
194
|
+
sock_sendfile(fd, file, 0, file_data.st_size);
|
195
|
+
}
|
196
|
+
return NULL;
|
197
|
+
}
|
198
|
+
busy_no_file:
|
199
|
+
sock_write(fd,
|
200
|
+
"HTTP/1.1 503 Service Unavailable\r\nContent-Length: "
|
201
|
+
"13\r\n\r\nServer Busy.",
|
202
|
+
68);
|
203
|
+
return NULL;
|
204
|
+
}
|
205
|
+
|
206
|
+
/* *****************************************************************************
|
207
|
+
HTTP/1.1 protocol bare-bones implementation
|
208
|
+
*/
|
209
|
+
|
210
|
+
#define HTTP_BODY_CHUNK_SIZE 3072 // 4096
|
211
|
+
|
212
|
+
/* parse and call callback */
|
213
|
+
static void http1_on_data(intptr_t uuid, http1_protocol_s* protocol) {
|
214
|
+
ssize_t len = 0;
|
215
|
+
ssize_t result;
|
216
|
+
char buff[HTTP_BODY_CHUNK_SIZE];
|
217
|
+
char* buffer;
|
218
|
+
http_request_s* request = &protocol->request;
|
219
|
+
for (;;) {
|
220
|
+
// handle requests with no file data
|
221
|
+
if (request->body_file <= 0) {
|
222
|
+
// request headers parsing
|
223
|
+
if (len == 0) {
|
224
|
+
buffer = protocol->buffer;
|
225
|
+
// make sure headers don't overflow
|
226
|
+
len = sock_read(uuid, buffer + protocol->buffer_pos,
|
227
|
+
HTTP1_MAX_HEADER_SIZE - protocol->buffer_pos);
|
228
|
+
// update buffer read position.
|
229
|
+
protocol->buffer_pos += len;
|
230
|
+
}
|
231
|
+
if (len <= 0) {
|
232
|
+
return;
|
233
|
+
}
|
234
|
+
// parse headers
|
235
|
+
result =
|
236
|
+
http1_parse_request_headers(buffer, protocol->buffer_pos, request);
|
237
|
+
// review result
|
238
|
+
if (result >= 0) { // headers comeplete
|
239
|
+
// mark buffer position, for HTTP pipelining
|
240
|
+
protocol->buffer_pos = result;
|
241
|
+
// are we done?
|
242
|
+
if (request->content_length == 0 || request->body_str) {
|
243
|
+
goto handle_request;
|
244
|
+
}
|
245
|
+
if (request->content_length > protocol->settings->max_body_size) {
|
246
|
+
goto body_to_big;
|
247
|
+
}
|
248
|
+
// initialize or submit body data
|
249
|
+
result =
|
250
|
+
http1_parse_request_body(buffer + result, len - result, request);
|
251
|
+
if (result >= 0) {
|
252
|
+
protocol->buffer_pos += result;
|
253
|
+
goto handle_request;
|
254
|
+
} else if (result == -1) // parser error
|
255
|
+
goto parser_error;
|
256
|
+
goto parse_body;
|
257
|
+
} else if (result == -1) // parser error
|
258
|
+
goto parser_error;
|
259
|
+
// assume incomplete (result == -2), even if wrong, we're right.
|
260
|
+
len = 0;
|
261
|
+
continue;
|
262
|
+
}
|
263
|
+
if (request->body_file > 0) {
|
264
|
+
parse_body:
|
265
|
+
buffer = buff;
|
266
|
+
// request body parsing
|
267
|
+
len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
|
268
|
+
if (len <= 0)
|
269
|
+
return;
|
270
|
+
result = http1_parse_request_body(buffer, len, request);
|
271
|
+
if (result >= 0) {
|
272
|
+
// set buffer pos for piplining support
|
273
|
+
protocol->buffer_pos = result;
|
274
|
+
goto handle_request;
|
275
|
+
} else if (result == -1) // parser error
|
276
|
+
goto parser_error;
|
277
|
+
if (len < HTTP_BODY_CHUNK_SIZE) // pause parser for more data
|
278
|
+
return;
|
279
|
+
goto parse_body;
|
280
|
+
}
|
281
|
+
continue;
|
282
|
+
handle_request:
|
283
|
+
// review required headers / data
|
284
|
+
if (request->host == NULL)
|
285
|
+
goto bad_request;
|
286
|
+
http_settings_s* settings = protocol->settings;
|
287
|
+
// call request callback
|
288
|
+
if (protocol && settings &&
|
289
|
+
(protocol->settings->public_folder == NULL ||
|
290
|
+
http_response_sendfile2(NULL, request, settings->public_folder,
|
291
|
+
settings->public_folder_length, request->path,
|
292
|
+
request->path_len, settings->log_static))) {
|
293
|
+
protocol->on_request(request);
|
294
|
+
}
|
295
|
+
// clear request state
|
296
|
+
http_request_clear(request);
|
297
|
+
// rotate buffer for HTTP pipelining
|
298
|
+
if (result >= len) {
|
299
|
+
len = 0;
|
300
|
+
} else {
|
301
|
+
memmove(protocol->buffer, buffer + protocol->buffer_pos, len - result);
|
302
|
+
len -= result;
|
303
|
+
}
|
304
|
+
// restart buffer position
|
305
|
+
protocol->buffer_pos = 0;
|
306
|
+
buffer = protocol->buffer;
|
307
|
+
}
|
308
|
+
// no routes lead here.
|
309
|
+
fprintf(stderr,
|
310
|
+
"I am lost on a deserted island, no code can reach me here :-)\n");
|
311
|
+
return; // How did we get here?
|
312
|
+
parser_error:
|
313
|
+
if (request->headers_count == request->metadata.max_headers)
|
314
|
+
goto too_big;
|
315
|
+
bad_request:
|
316
|
+
/* handle generally bad requests */
|
317
|
+
{
|
318
|
+
http_response_s response = http_response_init(request);
|
319
|
+
response.status = 400;
|
320
|
+
http_response_write_body(&response, "Bad Request", 11);
|
321
|
+
http_response_finish(&response);
|
322
|
+
sock_close(uuid);
|
323
|
+
protocol->buffer_pos = 0;
|
324
|
+
return;
|
325
|
+
}
|
326
|
+
too_big:
|
327
|
+
/* handle oversized headers */
|
328
|
+
{
|
329
|
+
http_response_s response = http_response_init(request);
|
330
|
+
response.status = 431;
|
331
|
+
http_response_write_body(&response, "Request Header Fields Too Large", 31);
|
332
|
+
http_response_finish(&response);
|
333
|
+
sock_close(uuid);
|
334
|
+
protocol->buffer_pos = 0;
|
335
|
+
return;
|
336
|
+
body_to_big:
|
337
|
+
/* handle oversized body */
|
338
|
+
{
|
339
|
+
http_response_s response = http_response_init(request);
|
340
|
+
response.status = 413;
|
341
|
+
http_response_write_body(&response, "Payload Too Large", 17);
|
342
|
+
http_response_finish(&response);
|
343
|
+
sock_close(uuid);
|
344
|
+
protocol->buffer_pos = 0;
|
345
|
+
return;
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
|
350
|
+
/* *****************************************************************************
|
351
|
+
HTTP/1.1 listenning API implementation
|
352
|
+
*/
|
353
|
+
|
354
|
+
#undef http1_listen
|
355
|
+
|
356
|
+
static void http1_on_init(http_settings_s* settings) {
|
357
|
+
if (settings->timeout == 0)
|
358
|
+
settings->timeout = 5;
|
359
|
+
if (settings->max_body_size == 0)
|
360
|
+
settings->max_body_size = HTTP_DEFAULT_BODY_LIMIT;
|
361
|
+
if (settings->public_folder) {
|
362
|
+
settings->public_folder_length = strlen(settings->public_folder);
|
363
|
+
if (settings->public_folder[0] == '~' &&
|
364
|
+
settings->public_folder[1] == '/' && getenv("HOME")) {
|
365
|
+
char* home = getenv("HOME");
|
366
|
+
size_t home_len = strlen(home);
|
367
|
+
char* tmp = malloc(settings->public_folder_length + home_len + 1);
|
368
|
+
memcpy(tmp, home, home_len);
|
369
|
+
if (home[home_len - 1] == '/')
|
370
|
+
--home_len;
|
371
|
+
memcpy(tmp + home_len, settings->public_folder + 1,
|
372
|
+
settings->public_folder_length); // copy also the NULL
|
373
|
+
settings->public_folder = tmp;
|
374
|
+
settings->private_metaflags |= 1;
|
375
|
+
settings->public_folder_length = strlen(settings->public_folder);
|
376
|
+
}
|
377
|
+
}
|
378
|
+
}
|
379
|
+
static void http1_on_finish(http_settings_s* settings) {
|
380
|
+
if (settings->private_metaflags & 1)
|
381
|
+
free((void*)settings->public_folder);
|
382
|
+
if (settings->private_metaflags & 2)
|
383
|
+
free(settings);
|
384
|
+
}
|
385
|
+
|
386
|
+
int http1_listen(const char* port,
|
387
|
+
const char* address,
|
388
|
+
http_settings_s settings) {
|
389
|
+
if (settings.on_request == NULL) {
|
390
|
+
fprintf(
|
391
|
+
stderr,
|
392
|
+
"ERROR: http1_listen requires the .on_request parameter to be set\n");
|
393
|
+
exit(11);
|
394
|
+
}
|
395
|
+
http_settings_s* settings_copy = malloc(sizeof(*settings_copy));
|
396
|
+
*settings_copy = settings;
|
397
|
+
settings_copy->private_metaflags = 2;
|
398
|
+
return server_listen(.port = port, .address = address,
|
399
|
+
.on_start = (void*)http1_on_init,
|
400
|
+
.on_finish = (void*)http1_on_finish,
|
401
|
+
.on_open = (void*)http1_alloc, .udata = settings_copy);
|
402
|
+
}
|