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.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. 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
@@ -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
+ }