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.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -3
  4. data/bin/config.ru +23 -2
  5. data/bin/http-hello +1 -1
  6. data/bin/ws-shootout +5 -0
  7. data/ext/iodine/defer.c +468 -0
  8. data/ext/iodine/defer.h +105 -0
  9. data/ext/iodine/evio.c +263 -0
  10. data/ext/iodine/evio.h +133 -0
  11. data/ext/iodine/extconf.rb +2 -1
  12. data/ext/iodine/facil.c +958 -0
  13. data/ext/iodine/facil.h +423 -0
  14. data/ext/iodine/http.c +90 -0
  15. data/ext/iodine/http.h +50 -12
  16. data/ext/iodine/http1.c +200 -267
  17. data/ext/iodine/http1.h +17 -26
  18. data/ext/iodine/http1_request.c +81 -0
  19. data/ext/iodine/http1_request.h +58 -0
  20. data/ext/iodine/http1_response.c +403 -0
  21. data/ext/iodine/http1_response.h +90 -0
  22. data/ext/iodine/http1_simple_parser.c +124 -108
  23. data/ext/iodine/http1_simple_parser.h +8 -3
  24. data/ext/iodine/http_request.c +104 -0
  25. data/ext/iodine/http_request.h +58 -102
  26. data/ext/iodine/http_response.c +212 -208
  27. data/ext/iodine/http_response.h +89 -252
  28. data/ext/iodine/iodine_core.c +57 -46
  29. data/ext/iodine/iodine_core.h +3 -1
  30. data/ext/iodine/iodine_http.c +105 -81
  31. data/ext/iodine/iodine_websocket.c +17 -13
  32. data/ext/iodine/iodine_websocket.h +1 -0
  33. data/ext/iodine/rb-call.c +9 -7
  34. data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
  35. data/ext/iodine/rb-rack-io.c +12 -6
  36. data/ext/iodine/rb-rack-io.h +1 -1
  37. data/ext/iodine/rb-registry.c +5 -2
  38. data/ext/iodine/sock.c +1159 -0
  39. data/ext/iodine/{libsock.h → sock.h} +138 -142
  40. data/ext/iodine/spnlock.inc +77 -0
  41. data/ext/iodine/websockets.c +101 -112
  42. data/ext/iodine/websockets.h +38 -19
  43. data/iodine.gemspec +3 -3
  44. data/lib/iodine/version.rb +1 -1
  45. data/lib/rack/handler/iodine.rb +6 -6
  46. metadata +23 -19
  47. data/ext/iodine/http_response_http1.h +0 -382
  48. data/ext/iodine/libasync.c +0 -570
  49. data/ext/iodine/libasync.h +0 -122
  50. data/ext/iodine/libreact.c +0 -350
  51. data/ext/iodine/libreact.h +0 -244
  52. data/ext/iodine/libserver.c +0 -957
  53. data/ext/iodine/libserver.h +0 -481
  54. data/ext/iodine/libsock.c +0 -1025
  55. data/ext/iodine/spnlock.h +0 -243
@@ -1,244 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2016-2017
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #ifndef LIB_REACT
8
- #define LIB_REACT "0.3.0"
9
- #define LIB_REACT_VERSION_MAJOR 0
10
- #define LIB_REACT_VERSION_MINOR 3
11
- #define LIB_REACT_VERSION_PATCH 0
12
-
13
- #ifndef _GNU_SOURCE
14
- #define _GNU_SOURCE
15
- #endif
16
-
17
- #ifndef REACTOR_MAX_EVENTS
18
- #define REACTOR_MAX_EVENTS 64
19
- #endif
20
- #ifndef REACTOR_TICK
21
- #define REACTOR_TICK 256 /** in milliseconsd */
22
- #endif
23
-
24
- #include <stdint.h>
25
- #include <sys/time.h>
26
- #include <sys/types.h>
27
- #include <unistd.h>
28
-
29
- #if !defined(__unix__) && !defined(__linux__) && !defined(__APPLE__) && \
30
- !defined(__CYGWIN__)
31
- #error This library currently supports only Unix based systems (i.e. Linux and BSD)
32
- #endif
33
-
34
- /* until linux supports KQueue, which might not happen... */
35
- #if !defined(__linux__) && !defined(__CYGWIN__)
36
- #define reactor_epoll 1
37
- #define reactor_kqueue 0
38
- #else
39
- #define reactor_epoll 0
40
- #define reactor_kqueue 1
41
- #endif
42
-
43
- /* *****************************************************************************
44
- A simple, predictable UUID for file-descriptors, for collision prevention
45
-
46
- It's protected against duplicate definition (i.e., when including `libsock.h`)
47
- */
48
- #ifndef FD_UUID_TYPE_DEFINED
49
- #define FD_UUID_TYPE_DEFINED
50
- /** fduuid_u is used to identify a specific connection, helping to manage file
51
- * descriptor collisions (when a new connection receives an old connection's
52
- * file descriptor), especially when the `on_close` event is fired after an
53
- * `accept` was called and the old file descriptor was already recycled.
54
- *
55
- * This requires that sizeof(int) < sizeof(uintptr_t) or sizeof(int)*8 >= 32
56
- */
57
- typedef union {
58
- intptr_t uuid;
59
- struct {
60
- int fd : (sizeof(int) < sizeof(intptr_t) ? (sizeof(int) * 8) : 24);
61
- unsigned counter : (sizeof(int) < sizeof(intptr_t)
62
- ? ((sizeof(intptr_t) - sizeof(int)) * 8)
63
- : ((sizeof(intptr_t) * 8) - 24));
64
- } data;
65
- } fduuid_u;
66
-
67
- #define FDUUID_FAIL(uuid) (uuid == -1)
68
- #define sock_uuid2fd(uuid) ((fduuid_u *)(&uuid))->data.fd
69
- #endif
70
-
71
- /*****************************/ /** \file
72
- This small library implements a reactor pattern using callbacks.
73
-
74
- Here are the supported events and their callbacks:
75
-
76
- - File Descriptor events:
77
-
78
- - Ready to Read (`reactor_on_data` callback).
79
-
80
- - Ready to Write (`reactor_on_ready` callback).
81
-
82
- - Closed (`reactor_on_close` callback).
83
-
84
- Here's a quick example for an HTTP hello world (no HTTP parsing required)...:
85
-
86
- #include "libreact.h" // the reactor library
87
- #include "libsock.h" // easy socket functions, also allows integration.
88
-
89
- // a global server socket
90
- int srvfd = -1;
91
- // a global running flag
92
- int run = 1;
93
-
94
- // create the callback. This callback will be global and hardcoded,
95
- // so there are no runtime function pointer resolutions.
96
- void reactor_on_data(int fd) {
97
- if (fd == srvfd) {
98
- int new_client;
99
- // accept connections.
100
- while ((new_client = sock_accept(fd)) > 0) {
101
- fprintf(stderr, "Accepted new connetion\n");
102
- reactor_add(new_client);
103
- }
104
- fprintf(stderr, "No more clients... (or error)?\n");
105
- } else {
106
- fprintf(stderr, "Handling incoming data.\n");
107
- // handle data
108
- char data[1024];
109
- ssize_t len;
110
- while ((len = sock_read(fd, data, 1024)) > 0)
111
- sock_write(fd,
112
- "HTTP/1.1 200 OK\r\n"
113
- "Content-Length: 12\r\n"
114
- "Connection: keep-alive\r\n"
115
- "Keep-Alive: 1;timeout=5\r\n"
116
- "\r\n"
117
- "Hello World!",
118
- 100);
119
- }
120
- }
121
-
122
- void reactor_on_close(int fd) {
123
- fprintf(stderr, "%d closed the connection.\n", fd);
124
- }
125
-
126
- void stop_signal(int sig) {
127
- run = 0;
128
- signal(sig, SIG_DFL);
129
- }
130
- int main() {
131
- srvfd = sock_listen(NULL, "3000");
132
- signal(SIGINT, stop_signal);
133
- signal(SIGTERM, stop_signal);
134
- sock_lib_init();
135
- reactor_init();
136
- reactor_add(srvfd);
137
- fprintf(stderr, "Starting reactor loop\n");
138
- while (run && reactor_review() >= 0)
139
- ;
140
- fprintf(stderr, "\nGoodbye.\n");
141
- }
142
-
143
- */
144
-
145
- /* *****************************************************************************
146
- Initialization and global workflow.
147
- */
148
-
149
- /**
150
- Initializes the processes reactor object.
151
-
152
- Reactor objects are a per-process object. Avoid forking a process with an active
153
- reactor, as some unexpected results might occur.
154
-
155
- Returns -1 on error, otherwise returns 0.
156
- */
157
- ssize_t reactor_init();
158
- /**
159
- Reviews any pending events (up to REACTOR_MAX_EVENTS) and calls any callbacks.
160
-
161
- Returns -1 on error, otherwise returns the number of events handled by the
162
- reactor.
163
- */
164
- int reactor_review();
165
- /**
166
- Closes the reactor, releasing it's resources.
167
- */
168
- void reactor_stop();
169
-
170
- /* *****************************************************************************
171
- Adding and removing normal file descriptors.
172
- */
173
-
174
- /**
175
- Adds a file descriptor to the reactor, so that callbacks will be called for it's
176
- events.
177
-
178
- Returns -1 on error, otherwise return value is system dependent.
179
- */
180
- int reactor_add(intptr_t uuid);
181
-
182
- /**
183
- Adds a timer file descriptor, so that callbacks will be called for it's events.
184
-
185
- Returns -1 on error, otherwise return value is system dependent.
186
- */
187
- int reactor_add_timer(intptr_t uuid, long milliseconds);
188
- /**
189
- Removes a file descriptor from the reactor - further callbacks for this file
190
- descriptor won't be called.
191
-
192
- Returns -1 on error, otherwise return value is system dependent. If the file
193
- descriptor wasn't owned by the reactor, it isn't an error.
194
- */
195
- int reactor_remove(intptr_t uuid);
196
- /**
197
- Removes a timer file descriptor from the reactor - further callbacks for this
198
- file descriptor's timer events won't be called.
199
-
200
- Returns -1 on error, otherwise return value is system dependent. If the file
201
- descriptor wasn't owned by the reactor, it isn't an error.
202
- */
203
- int reactor_remove_timer(intptr_t uuid);
204
- /**
205
- Closes a file descriptor, calling it's callback if it was registered with the
206
- reactor.
207
- */
208
- void reactor_close(intptr_t uuid);
209
-
210
- /* *****************************************************************************
211
- Timers.
212
- */
213
-
214
- /**
215
- Adds a file descriptor as a timer object.
216
-
217
- Returns -1 on error, otherwise return value is system dependent.
218
- */
219
- int reactor_add_timer(intptr_t uuid, long milliseconds);
220
- /**
221
- epoll requires the timer to be "reset" before repeating. Kqueue requires no such
222
- thing.
223
-
224
- This method promises that the timer will be repeated when running on epoll. This
225
- method is redundent on kqueue.
226
- */
227
- void reactor_reset_timer(intptr_t uuid);
228
- /**
229
- Creates a timer file descriptor, system dependent.
230
-
231
- Returns -1 on error, or a valid fd on success (not an fd UUID, as these are
232
- controlled by `libsock` and `libreact` can be used independently as well).
233
- */
234
- intptr_t reactor_make_timer();
235
-
236
- #if defined(__cplusplus)
237
- extern "C" {
238
- #endif
239
-
240
- #if defined(__cplusplus)
241
- } /* extern "C" */
242
- #endif
243
-
244
- #endif /* end of include guard: LIB_REACT */
@@ -1,957 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2016-2017
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #ifndef _GNU_SOURCE
8
- #define _GNU_SOURCE
9
- #endif
10
- #include "libserver.h"
11
- #include <errno.h>
12
- #include <pthread.h>
13
- #include <signal.h>
14
- #include <string.h>
15
- #include <sys/mman.h>
16
- #include <sys/wait.h>
17
-
18
- /* *****************************************************************************
19
- Connection Data
20
- */
21
- typedef struct {
22
- protocol_s *protocol;
23
- time_t active;
24
- uint8_t timeout;
25
- spn_lock_i lock;
26
- } fd_data_s;
27
-
28
- /*
29
- These macros mean we won't need to change code if we change the locking system.
30
- */
31
-
32
- #define lock_fd_init(fd) (fd)->lock = SPN_LOCK_INIT
33
- #define lock_fd_destroy(fd) spn_unlock(&((fd)->lock))
34
- /** returns 0 on success, value on failure */
35
- #define try_lock_fd(fd) spn_trylock(&((fd)->lock))
36
- #define lock_fd(fd) spn_lock(&((fd)->lock))
37
- #define unlock_fd(fd) spn_unlock(&((fd)->lock))
38
- #define clear_fd_data(fd_data) \
39
- { *(fd_data) = (fd_data_s){.lock = (fd_data)->lock}; }
40
-
41
- /* *****************************************************************************
42
- Server Core Data
43
- */
44
- static struct {
45
- fd_data_s *fds;
46
- time_t last_tick;
47
- void (*on_idle)(void);
48
- size_t capacity;
49
- uint8_t running;
50
- } server_data = {.fds = NULL};
51
-
52
- /*
53
- These macros help prevent code changes when changing the data struct.
54
- */
55
-
56
- #define valid_uuid(uuid) sock_isvalid(uuid)
57
-
58
- #define fd_data(fd) server_data.fds[(fd)]
59
-
60
- #define uuid_data(uuid) fd_data(sock_uuid2fd(uuid))
61
-
62
- #define clear_uuid(uuid) clear_fd_data(server_data.fds + sock_uuid2fd(uuid))
63
-
64
- #define protocol_fd(fd) (server_data.fds[(fd)].protocol)
65
- #define protocol_uuid(uuid) protocol_fd(sock_uuid2fd(uuid))
66
-
67
- #define fduuid_get(ifd) (server_data.fds[(ifd)].uuid)
68
-
69
- #define protocol_is_busy(protocol) \
70
- spn_is_locked(&(((protocol_s *)(protocol))->callback_lock))
71
- #define protocol_unset_busy(protocol) \
72
- spn_unlock(&(((protocol_s *)(protocol))->callback_lock))
73
- #define protocol_set_busy(protocol) \
74
- spn_trylock(&(((protocol_s *)(protocol))->callback_lock))
75
-
76
- #define try_lock_uuid(uuid) try_lock_fd(server_data.fds + sock_uuid2fd(uuid))
77
- #define lock_uuid(uuid) lock_fd(server_data.fds + sock_uuid2fd(uuid))
78
- #define unlock_uuid(uuid) unlock_fd(server_data.fds + sock_uuid2fd(uuid))
79
-
80
- // run through any open sockets and call the shutdown handler
81
- static inline void server_on_shutdown(void) {
82
- if (server_data.fds && server_data.capacity > 0) {
83
- intptr_t uuid;
84
- for (size_t i = 0; i < server_data.capacity; i++) {
85
- if (server_data.fds[i].protocol == NULL)
86
- continue;
87
- uuid = sock_fd2uuid(i);
88
- if (uuid != -1) {
89
- if (server_data.fds[i].protocol->on_shutdown != NULL)
90
- server_data.fds[i].protocol->on_shutdown(uuid,
91
- server_data.fds[i].protocol);
92
- sock_close(uuid);
93
- sock_flush_strong(uuid);
94
- }
95
- }
96
- }
97
- }
98
-
99
- static void server_cleanup(void) {
100
- // run through any open sockets and call the shutdown handler
101
- server_on_shutdown();
102
- // free any lock objects (no need to change code if changing locking systems)
103
- for (size_t i = 0; i < server_data.capacity - 1; i++) {
104
- lock_fd_destroy(server_data.fds + i);
105
- server_data.fds[i] = (fd_data_s){.protocol = NULL};
106
- }
107
- // free memory
108
- if (server_data.fds) {
109
- munmap(server_data.fds, sizeof(fd_data_s) * server_data.capacity);
110
- server_data.fds = NULL;
111
- }
112
- }
113
-
114
- static void init_server(void) {
115
- pthread_mutex_t inner_lock = PTHREAD_MUTEX_INITIALIZER;
116
- pthread_mutex_lock(&inner_lock);
117
- if (server_data.fds == NULL) {
118
- server_data.capacity = sock_max_capacity();
119
- atexit(server_cleanup);
120
- server_data.fds = mmap(NULL, sizeof(fd_data_s) * server_data.capacity,
121
- PROT_READ | PROT_WRITE | PROT_EXEC,
122
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
123
- for (size_t i = 0; i < server_data.capacity - 1; i++) {
124
- server_data.fds[i] = (fd_data_s){.protocol = NULL};
125
- lock_fd_init(server_data.fds + i);
126
- }
127
- }
128
- pthread_mutex_unlock(&inner_lock);
129
- }
130
-
131
- /** initializes the library if it wasn't already initialized. */
132
- #define validate_mem() \
133
- { \
134
- if (server_data.fds == NULL) \
135
- init_server(); \
136
- }
137
-
138
- /* *****************************************************************************
139
- `libsock` Callback Implementation
140
- */
141
-
142
- void sock_touch(intptr_t uuid) {
143
- if (server_data.fds != NULL)
144
- uuid_data(uuid).active = server_data.last_tick;
145
- }
146
-
147
- /* *****************************************************************************
148
- The Reactor Callback Implementation
149
- */
150
-
151
- void reactor_on_close_async(void *_pr) {
152
- if (protocol_set_busy(_pr) == 0) {
153
- ((protocol_s *)_pr)->on_close(_pr);
154
- return;
155
- }
156
- async_run(reactor_on_close_async, _pr);
157
- }
158
-
159
- void reactor_on_close(intptr_t uuid) {
160
- if (server_data.fds) {
161
- // get the currect state
162
- lock_uuid(uuid);
163
- protocol_s *protocol = protocol_uuid(uuid);
164
- // clear state
165
- clear_uuid(uuid);
166
- unlock_uuid(uuid);
167
- // call callback
168
- if (protocol && protocol->on_close)
169
- reactor_on_close_async(protocol);
170
- }
171
- }
172
-
173
- void reactor_on_data_async(void *_fduuid) {
174
- intptr_t fduuid = (intptr_t)_fduuid;
175
- if (!valid_uuid(fduuid) || protocol_uuid(fduuid) == NULL)
176
- return;
177
- // try to lock the socket
178
- if (try_lock_uuid(fduuid))
179
- goto no_lock;
180
- // get current state (protocol might have changed during this time)
181
- protocol_s *protocol = protocol_uuid(fduuid);
182
- // review protocol and get use privilage
183
- if (protocol == NULL || protocol_set_busy(protocol)) {
184
- // fprintf(stderr, "fduuid is busy %p\n", _fduuid);
185
- unlock_uuid(fduuid);
186
- goto no_lock;
187
- }
188
- // unlock
189
- unlock_uuid(fduuid);
190
- // fire event
191
- if (protocol && protocol->on_data)
192
- protocol->on_data(fduuid, protocol);
193
- // clear the original busy flag
194
- protocol_unset_busy(protocol);
195
- return;
196
- no_lock:
197
- // fprintf(stderr, "no lock for %p\n", _fduuid);
198
- // failed to aquire lock / busy
199
- async_run(reactor_on_data_async, _fduuid);
200
- }
201
-
202
- void reactor_on_data(intptr_t fd) {
203
- async_run(reactor_on_data_async, (void *)fd);
204
- }
205
-
206
- void reactor_on_ready(intptr_t uuid) {
207
- uuid_data(uuid).active = server_data.last_tick;
208
- lock_uuid(uuid);
209
- protocol_s *protocol = protocol_uuid(uuid);
210
- unlock_uuid(uuid);
211
- if (protocol && protocol->on_ready && !sock_packets_pending(uuid))
212
- protocol->on_ready(uuid, protocol);
213
- }
214
-
215
- /* *****************************************************************************
216
- Zombie Reaping
217
- With thanks to Dr Graham D Shaw.
218
- http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html
219
- */
220
-
221
- void reap_child_handler(int sig) {
222
- (void)(sig);
223
- int old_errno = errno;
224
- while (waitpid(-1, NULL, WNOHANG) > 0)
225
- ;
226
- errno = old_errno;
227
- }
228
-
229
- inline static void reap_children(void) {
230
- struct sigaction sa;
231
- sa.sa_handler = reap_child_handler;
232
- sigemptyset(&sa.sa_mask);
233
- sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
234
- if (sigaction(SIGCHLD, &sa, 0) == -1) {
235
- perror("Child reaping initialization failed");
236
- exit(1);
237
- }
238
- }
239
-
240
- /* *****************************************************************************
241
- Exit Signal Handling
242
- */
243
-
244
- static void stop_server_handler(int sig) {
245
- server_data.running = 0;
246
- async_signal();
247
- #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
248
- fprintf(stderr, " --- Stop signal received ---\n");
249
- #endif
250
- signal(sig, SIG_DFL);
251
- }
252
-
253
- inline static void listen_for_stop_signal(void) {
254
- struct sigaction sa;
255
- sa.sa_handler = stop_server_handler;
256
- sigemptyset(&sa.sa_mask);
257
- sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
258
- if (sigaction(SIGINT, &sa, 0) || sigaction(SIGTERM, &sa, 0)) {
259
- perror("Signal registration failed");
260
- exit(2);
261
- }
262
- }
263
-
264
- /* *****************************************************************************
265
- The Listening Protocol
266
- */
267
-
268
- static const char *listener_protocol_name = "listening protocol __internal__";
269
-
270
- struct ListenerProtocol {
271
- protocol_s protocol;
272
- protocol_s *(*on_open)(intptr_t uuid, void *udata);
273
- void *udata;
274
- void (*on_start)(void *udata);
275
- void (*on_finish)(void *udata);
276
- };
277
-
278
- static void listener_on_data(intptr_t uuid, protocol_s *_listener) {
279
- intptr_t new_client;
280
- struct ListenerProtocol *listener = (void *)_listener;
281
- while ((new_client = sock_accept(uuid)) != -1) {
282
- // assume that sock_accept calls if needed
283
- // it's a clean slate in reactor_on_close ...
284
- // lock_uuid(new_client);
285
- // clear_uuid(new_client);
286
- // unlock_uuid(new_client);
287
- protocol_uuid(new_client) = listener->on_open(new_client, listener->udata);
288
- if (protocol_uuid(new_client)) {
289
- uuid_data(new_client).active = server_data.last_tick;
290
- protocol_unset_busy(protocol_uuid(new_client));
291
- reactor_add(new_client);
292
- continue;
293
- } else {
294
- sock_close(new_client);
295
- }
296
- }
297
- }
298
-
299
- static void free_listenner(void *_li) { free(_li); }
300
-
301
- static void listener_on_close(protocol_s *_listener) {
302
- if (((struct ListenerProtocol *)_listener)->on_finish)
303
- ((struct ListenerProtocol *)_listener)
304
- ->on_finish(((struct ListenerProtocol *)_listener)->udata);
305
- free_listenner(_listener);
306
- }
307
-
308
- static inline struct ListenerProtocol *
309
- listener_alloc(struct ServerServiceSettings settings) {
310
- struct ListenerProtocol *listener = malloc(sizeof(*listener));
311
- if (listener) {
312
- *listener = (struct ListenerProtocol){
313
- .protocol.service = listener_protocol_name,
314
- .protocol.on_data = listener_on_data,
315
- .protocol.on_close = listener_on_close,
316
- .on_open = settings.on_open,
317
- .udata = settings.udata,
318
- .on_start = settings.on_start,
319
- .on_finish = settings.on_finish,
320
- };
321
- return listener;
322
- }
323
- return NULL;
324
- }
325
-
326
- inline static void listener_on_server_start(void) {
327
- for (size_t i = 0; i < server_data.capacity; i++) {
328
- if (protocol_fd(i) && protocol_fd(i)->service == listener_protocol_name) {
329
- if (reactor_add(sock_fd2uuid(i)))
330
- perror("Couldn't register listening socket"), exit(4);
331
- // call the on_init callback
332
- if (((struct ListenerProtocol *)protocol_fd(i))->on_start)
333
- ((struct ListenerProtocol *)protocol_fd(i))
334
- ->on_start(((struct ListenerProtocol *)protocol_fd(i))->udata);
335
- }
336
- }
337
- }
338
- inline static void listener_on_server_shutdown(void) {
339
- for (size_t i = 0; i < server_data.capacity; i++) {
340
- if (protocol_fd(i) && protocol_fd(i)->service == listener_protocol_name) {
341
- sock_close(sock_fd2uuid(i));
342
- }
343
- }
344
- }
345
-
346
- /* *****************************************************************************
347
- * The timer protocol
348
- */
349
-
350
- /* *******
351
- Timer Protocol
352
- ******* */
353
- typedef struct {
354
- protocol_s protocol;
355
- size_t milliseconds;
356
- size_t repetitions;
357
- void (*task)(void *);
358
- void (*on_finish)(void *);
359
- void *arg;
360
- } timer_protocol_s;
361
-
362
- #define prot2timer(protocol) (*((timer_protocol_s *)(protocol)))
363
-
364
- const char *timer_protocol_name = "timer protocol __internal__";
365
-
366
- static void timer_on_data(intptr_t uuid, protocol_s *protocol) {
367
- prot2timer(protocol).task(prot2timer(protocol).arg);
368
- if (prot2timer(protocol).repetitions) {
369
- prot2timer(protocol).repetitions -= 1;
370
- if (prot2timer(protocol).repetitions == 0) {
371
- // fprintf(stderr, "closing timer?\n");
372
- reactor_remove_timer(uuid);
373
- sock_force_close(uuid);
374
- }
375
- }
376
- reactor_reset_timer(uuid);
377
- }
378
-
379
- static void timer_on_close(protocol_s *protocol) {
380
- // fprintf(stderr, "timer closed\n");
381
- if (prot2timer(protocol).on_finish)
382
- prot2timer(protocol).on_finish(prot2timer(protocol).arg);
383
- free(protocol);
384
- }
385
-
386
- static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
387
- size_t milliseconds,
388
- size_t repetitions,
389
- void (*on_finish)(void *)) {
390
- timer_protocol_s *t = malloc(sizeof(*t));
391
- if (t)
392
- *t = (timer_protocol_s){
393
- .protocol.service = timer_protocol_name,
394
- .protocol.on_data = timer_on_data,
395
- .protocol.on_close = timer_on_close,
396
- .arg = arg,
397
- .task = task,
398
- .on_finish = on_finish,
399
- .milliseconds = milliseconds,
400
- .repetitions = repetitions,
401
- };
402
- return t;
403
- }
404
-
405
- inline static void timer_on_server_start(void) {
406
- for (size_t i = 0; i < server_data.capacity; i++) {
407
- if (protocol_fd(i) && protocol_fd(i)->service == timer_protocol_name) {
408
- if (reactor_add_timer(sock_fd2uuid(i),
409
- prot2timer(protocol_fd(i)).milliseconds))
410
- perror("Couldn't register a required timed event."), exit(4);
411
- }
412
- }
413
- }
414
-
415
- /* *****************************************************************************
416
- Reactor cycling and timeout handling
417
- */
418
-
419
- static inline void timeout_review(void) {
420
- static time_t review = 0;
421
- if (review >= server_data.last_tick)
422
- return;
423
- time(&review);
424
- for (size_t i = 0; i < server_data.capacity; i++) {
425
- if (protocol_fd(i) == NULL)
426
- continue; // Protocol objects are required for open connections.
427
- if (fd_data(i).timeout == 0) {
428
- if (protocol_fd(i) && protocol_fd(i)->service != listener_protocol_name &&
429
- protocol_fd(i)->service != timer_protocol_name &&
430
- review - fd_data(i).active > 300) {
431
- sock_close(sock_fd2uuid(i));
432
- }
433
- continue;
434
- }
435
- if (fd_data(i).active + fd_data(i).timeout < review) {
436
- if (protocol_fd(i)->ping) {
437
- protocol_fd(i)->ping(sock_fd2uuid(i), protocol_fd(i));
438
- } else if (!protocol_is_busy(protocol_fd(i)) ||
439
- (review - fd_data(i).active > 300)) {
440
- sock_close(sock_fd2uuid(i));
441
- }
442
- }
443
- }
444
- }
445
-
446
- static void server_cycle(void *unused) {
447
- (void)(unused);
448
- static int8_t perform_idle = 1;
449
-
450
- #if SERVER_DELAY_IO
451
- if (async_any()) {
452
- async_run(server_cycle, NULL);
453
- return;
454
- }
455
- #endif
456
-
457
- time(&server_data.last_tick);
458
- if (server_data.running) {
459
- timeout_review();
460
- int e_count = reactor_review();
461
- if (e_count < 0) {
462
- return;
463
- }
464
- if (e_count == 0) {
465
- if (perform_idle && server_data.on_idle)
466
- server_data.on_idle();
467
- perform_idle = 0;
468
- } else {
469
- perform_idle = 1;
470
- }
471
- async_run(server_cycle, NULL);
472
- }
473
- }
474
- /* *****************************************************************************
475
- * The Server API
476
- * (and helper functions)
477
- */
478
-
479
- /* *****************************************************************************
480
- * Server actions
481
- */
482
-
483
- #undef server_listen
484
- #undef server_run
485
- /**
486
- Listens to a server with the following server settings (which MUST include
487
- a default protocol).
488
-
489
- This method blocks the current thread until the server is stopped (either
490
- though a `srv_stop` function or when a SIGINT/SIGTERM is received).
491
- */
492
- int server_listen(struct ServerServiceSettings settings) {
493
- validate_mem();
494
- if (settings.on_open == NULL || settings.port == NULL)
495
- return -1;
496
- intptr_t fduuid = sock_listen(settings.address, settings.port);
497
- if (fduuid == -1)
498
- return -1;
499
- server_data.fds[sock_uuid2fd(fduuid)].protocol =
500
- (void *)listener_alloc(settings);
501
- if (server_data.fds[sock_uuid2fd(fduuid)].protocol == NULL)
502
- goto error;
503
- if (server_data.running && reactor_add(fduuid))
504
- goto error;
505
- #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
506
- fprintf(stderr, "* Listening on port %s\n", settings.port);
507
- #endif
508
- return 0;
509
- error:
510
- sock_close(fduuid);
511
- return -1;
512
- }
513
- /** runs the server, hanging the current process and thread. */
514
- ssize_t server_run(struct ServerSettings settings) {
515
- validate_mem();
516
- if (server_data.running) {
517
- return -1;
518
- }
519
- reap_children();
520
- listen_for_stop_signal();
521
- server_data.running = 1;
522
- server_data.on_idle = settings.on_idle;
523
- if (settings.processes == 0)
524
- settings.processes = 1;
525
-
526
- #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
527
- if (settings.threads == 0)
528
- fprintf(stderr, "* Running %u processes"
529
- " in single thread mode.\n",
530
- settings.processes);
531
- else
532
- fprintf(stderr, "* Running %u processes"
533
- " X %u threads.\n",
534
- settings.processes, settings.threads);
535
- #endif
536
-
537
- pid_t rootpid = getpid();
538
- pid_t *children = NULL;
539
- if (settings.processes > 1) {
540
- children = malloc(sizeof(*children) * settings.processes);
541
- for (size_t i = 0; i < (size_t)(settings.processes - 1); i++) {
542
- if (fork() == 0)
543
- break;
544
- }
545
- }
546
- if (reactor_init() < 0)
547
- perror("Reactor initialization failed"), exit(3);
548
- listener_on_server_start();
549
- timer_on_server_start();
550
- #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
551
- fprintf(stderr, "* [%d] Running.\n", getpid());
552
- #endif
553
- async_start(settings.threads);
554
- if (settings.on_init)
555
- settings.on_init();
556
- async_run(server_cycle, NULL);
557
- if (settings.threads > 0)
558
- async_join();
559
- else
560
- async_perform();
561
-
562
- /*
563
- * start a single worker thread for shutdown tasks and async close operations
564
- */
565
- async_start(1);
566
-
567
- listener_on_server_shutdown();
568
- reactor_review();
569
- server_on_shutdown();
570
- /* cycle until no IO events occure. */
571
- while (reactor_review() > 0)
572
- ;
573
- if (settings.on_finish)
574
- settings.on_finish();
575
-
576
- /*
577
- * Wait for any unfinished tasks.
578
- */
579
- async_finish();
580
-
581
- if (children) {
582
- if (rootpid == getpid()) {
583
- while (waitpid(-1, NULL, 0) >= 0)
584
- ;
585
- }
586
- free(children);
587
- }
588
- #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
589
- fprintf(stderr, "* [%d] Shutdown.\n", getpid());
590
- if (rootpid == getpid())
591
- fprintf(stderr, "* Shutdown process complete.\n");
592
- #endif
593
- if (rootpid != getpid())
594
- exit(0);
595
-
596
- return 0;
597
- }
598
-
599
- void server_stop(void) { server_data.running = 0; }
600
- /**
601
- Returns the last time the server reviewed any pending IO events.
602
- */
603
- time_t server_last_tick(void) { return server_data.last_tick; }
604
-
605
- /* *****************************************************************************
606
- * Socket actions
607
- */
608
-
609
- /**
610
- Sets a new active protocol object for the requested file descriptor.
611
-
612
- This also schedules the old protocol's `on_close` callback to run, making sure
613
- all resources are released.
614
-
615
- Returns -1 on error (i.e. connection closed), otherwise returns 0.
616
- */
617
- ssize_t server_switch_protocol(intptr_t fd, protocol_s *new_protocol) {
618
- if (new_protocol == NULL || valid_uuid(fd) == 0)
619
- return -1;
620
- protocol_s *old_protocol;
621
- lock_uuid(fd);
622
- old_protocol = uuid_data(fd).protocol;
623
- uuid_data(fd).protocol = new_protocol;
624
- unlock_uuid(fd);
625
- if (old_protocol && old_protocol->on_close)
626
- reactor_on_close_async(old_protocol);
627
- return 0;
628
- }
629
- /**
630
- Gets the active protocol object for the requested file descriptor.
631
-
632
- Returns NULL on error (i.e. connection closed), otherwise returns a `protocol_s`
633
- pointer.
634
- */
635
- protocol_s *server_get_protocol(intptr_t uuid) {
636
- if (valid_uuid(uuid) == 0)
637
- return NULL;
638
- protocol_s *protocol;
639
- lock_uuid(uuid);
640
- protocol = uuid_data(uuid).protocol;
641
- unlock_uuid(uuid);
642
- return protocol;
643
- }
644
- /**
645
- Sets a connection's timeout.
646
-
647
- Returns -1 on error (i.e. connection closed), otherwise returns 0.
648
- */
649
- void server_set_timeout(intptr_t fd, uint8_t timeout) {
650
- if (valid_uuid(fd) == 0) {
651
- return;
652
- }
653
- lock_uuid(fd);
654
- uuid_data(fd).active = server_data.last_tick;
655
- uuid_data(fd).timeout = timeout;
656
- unlock_uuid(fd);
657
- }
658
- /**
659
- Gets a connection's timeout, type of uint8_t.
660
-
661
- A value of 0 might mean that no timeout was set OR that the connection inquired
662
- about was invalid.
663
- */
664
- uint8_t server_get_timeout(intptr_t fd) {
665
- if (valid_uuid(fd) == 0)
666
- return 0;
667
- return uuid_data(fd).timeout;
668
- }
669
-
670
- /** Attaches an existing connection (fd) to the server's reactor and protocol
671
- management system, so that the server can be used also to manage connection
672
- based resources asynchronously (i.e. database resources etc').
673
-
674
- On failure the fduuid_u.data.fd value will be -1.
675
- */
676
- intptr_t server_attach(int fd, protocol_s *protocol) {
677
- intptr_t uuid = sock_open(fd);
678
- if (uuid == -1)
679
- return -1;
680
- protocol_fd(fd) = protocol;
681
- if (reactor_add(uuid)) {
682
- sock_close(uuid);
683
- return -1;
684
- }
685
- return uuid;
686
- }
687
- /** Hijack a socket (file descriptor) from the server, clearing up it's
688
- resources. The control of hte socket is totally relinquished.
689
-
690
- This method will block until all the data in the buffer is sent before
691
- releasing control of the socket.
692
-
693
- The returned value is the fd for the socket, or -1 on error.
694
- */
695
- int server_hijack(intptr_t uuid) {
696
- if (sock_isvalid(uuid) == 0)
697
- return -1;
698
- reactor_remove(uuid);
699
- sock_flush_strong(uuid);
700
- if (sock_isvalid(uuid) == 0)
701
- return -1;
702
- protocol_s *old_protocol;
703
- lock_uuid(uuid);
704
- old_protocol = uuid_data(uuid).protocol;
705
- uuid_data(uuid).protocol = NULL;
706
- unlock_uuid(uuid);
707
- if (old_protocol && old_protocol->on_close)
708
- reactor_on_close_async(old_protocol);
709
- return sock_uuid2fd(uuid);
710
- }
711
- /** Counts the number of connections for the specified protocol (NULL = all
712
- protocols). */
713
- long server_count(char *service) {
714
- long count = 0;
715
- if (service == NULL) {
716
- for (size_t i = 0; i < server_data.capacity; i++) {
717
- if (protocol_fd(i) && protocol_fd(i)->service != listener_protocol_name &&
718
- protocol_fd(i)->service != timer_protocol_name)
719
- count++;
720
- }
721
- } else {
722
- for (size_t i = 0; i < server_data.capacity; i++) {
723
- if (protocol_fd(i) && protocol_fd(i)->service == service)
724
- count++;
725
- }
726
- }
727
- return count;
728
- }
729
-
730
- /* *****************************************************************************
731
- * Connection Tasks (each and deffer tactics implementations)
732
- */
733
-
734
- /* *******
735
- Task core data
736
- ******* */
737
- typedef struct {
738
- intptr_t origin;
739
- intptr_t target;
740
- const char *service;
741
- void (*task)(intptr_t fd, protocol_s *protocol, void *arg);
742
- void *on_finish;
743
- void *arg;
744
- } srv_task_s;
745
-
746
- /* Get task from void pointer. */
747
- #define p2task(task) (*((srv_task_s *)(task)))
748
-
749
- /* Get fallback callback from the task object. */
750
- #define task2fallback(task) \
751
- ((void (*)(intptr_t, void *))(p2task(task).on_finish))
752
-
753
- /* Get on_finished callback from the task object. */
754
- #define task2on_done(task) \
755
- ((void (*)(intptr_t, protocol_s *, void *))(p2task(task).on_finish))
756
- /* allows for later implementation of a task pool with minimal code updates. */
757
- static inline srv_task_s *task_alloc(void) {
758
- return malloc(sizeof(srv_task_s));
759
- }
760
-
761
- /* allows for later implementation of a task pool with minimal code updates. */
762
- static inline void task_free(srv_task_s *task) { free(task); }
763
-
764
- /* performs a single connection task. */
765
- static void perform_single_task(void *task) {
766
- if (p2task(task).target < 0 || sock_isvalid(p2task(task).target) == 0) {
767
- if (p2task(task).on_finish) // an invalid connection fallback
768
- task2fallback(task)(p2task(task).origin, p2task(task).arg);
769
- task_free(task);
770
- return;
771
- }
772
- if (try_lock_uuid(p2task(task).target) == 0) {
773
- // get protocol
774
- protocol_s *protocol = protocol_uuid(p2task(task).target);
775
- if (protocol && protocol_set_busy(protocol) == 0) {
776
- // clear the original busy flag
777
- unlock_uuid(p2task(task).target);
778
- p2task(task).task(p2task(task).target, protocol, p2task(task).arg);
779
- protocol_unset_busy(protocol);
780
- task_free(task);
781
- return;
782
- }
783
- unlock_uuid(p2task(task).target);
784
- }
785
- async_run(perform_single_task, task);
786
- }
787
-
788
- /* performs a connection group task. */
789
- static void perform_each_task(void *task) {
790
- intptr_t uuid;
791
- protocol_s *protocol;
792
- while (p2task(task).target < (intptr_t)server_data.capacity) {
793
- uuid = sock_fd2uuid(p2task(task).target);
794
- if (uuid == -1 || uuid == p2task(task).origin) {
795
- ++p2task(task).target;
796
- continue;
797
- }
798
- if (try_lock_uuid(uuid) == 0) {
799
- protocol = protocol_uuid(uuid);
800
- if (protocol == NULL || protocol->service != p2task(task).service) {
801
- unlock_uuid(uuid);
802
- ++p2task(task).target;
803
- continue;
804
- } else if (protocol_set_busy(protocol) == 0) {
805
- // unlock uuid
806
- unlock_uuid(uuid);
807
- // perform task
808
- p2task(task).task(uuid, protocol, p2task(task).arg);
809
- // clear the busy flag
810
- protocol_unset_busy(protocol);
811
- // step forward
812
- ++p2task(task).target;
813
- continue;
814
- }
815
- // it's the right protocol and service, but we couldn't lock the protocol
816
- unlock_uuid(uuid);
817
- }
818
- async_run(perform_each_task, task);
819
- return;
820
- }
821
- if (p2task(task).on_finish) { // finished group task callback
822
- task2on_done(task)(p2task(task).origin,
823
- (sock_isvalid(p2task(task).origin)
824
- ? protocol_uuid(p2task(task).origin)
825
- : NULL),
826
- p2task(task).arg);
827
- }
828
- task_free(task);
829
- return;
830
- }
831
-
832
- /* *******
833
- API
834
- ******* */
835
-
836
- /**
837
- Performs a task for each connection except the origin connection, unsafely and
838
- synchronously.
839
- */
840
- void server_each_unsafe(intptr_t origin_uuid,
841
- void (*task)(intptr_t origin_uuid, intptr_t target_uuid,
842
- protocol_s *target_protocol, void *arg),
843
- void *arg) {
844
- intptr_t target;
845
- protocol_s *protocol;
846
- for (size_t i = 0; i < server_data.capacity; i++) {
847
- target = sock_fd2uuid(p2task(task).target);
848
- if (target == -1)
849
- continue;
850
- protocol = protocol_uuid(target);
851
- if (protocol == NULL || protocol->service == listener_protocol_name ||
852
- protocol->service == timer_protocol_name)
853
- continue;
854
- task(origin_uuid, target, protocol, arg);
855
- }
856
- }
857
-
858
- /**
859
- Schedules a specific task to run asyncronously for each connection (except the
860
- origin connection) on a specific protocol.
861
- */
862
- void server_each(intptr_t origin_fd, const char *service,
863
- void (*task)(intptr_t fd, protocol_s *protocol, void *arg),
864
- void *arg, void (*on_finish)(intptr_t fd, protocol_s *protocol,
865
- void *arg)) {
866
- srv_task_s *t = NULL;
867
- if (service == NULL || task == NULL)
868
- goto error;
869
- t = task_alloc();
870
- if (t == NULL)
871
- goto error;
872
- *t = (srv_task_s){.service = service,
873
- .origin = origin_fd,
874
- .task = task,
875
- .on_finish = (void *)on_finish,
876
- .arg = arg};
877
- if (async_run(perform_each_task, t))
878
- goto error;
879
- return;
880
- error:
881
- if (t)
882
- task_free(t);
883
- if (on_finish)
884
- on_finish(origin_fd,
885
- (sock_isvalid(origin_fd) ? protocol_uuid(origin_fd) : NULL), arg);
886
- }
887
-
888
- /** Schedules a specific task to run asyncronously for a specific connection.
889
- */
890
- void server_task(intptr_t caller_fd,
891
- void (*task)(intptr_t fd, protocol_s *protocol, void *arg),
892
- void *arg, void (*fallback)(intptr_t fd, void *arg)) {
893
- srv_task_s *t = NULL;
894
- if (task == NULL)
895
- goto error;
896
- t = task_alloc();
897
- if (t == NULL)
898
- goto error;
899
- *t = (srv_task_s){.target = caller_fd,
900
- .task = task,
901
- .on_finish = (void *)fallback,
902
- .arg = arg};
903
- if (async_run(perform_single_task, t))
904
- goto error;
905
- return;
906
- error:
907
- if (t)
908
- task_free(t);
909
- if (fallback)
910
- fallback(caller_fd, arg);
911
- }
912
-
913
- /* *****************************************************************************
914
- * Timed tasks
915
- */
916
-
917
- /** Creates a system timer (at the cost of 1 file descriptor) and pushes the
918
- timer to the reactor. The task will repeat `repetitions` times. if
919
- `repetitions` is set to 0, task will repeat forever. Returns -1 on error
920
- or the new file descriptor on succeess.
921
- */
922
- int server_run_every(size_t milliseconds, size_t repetitions,
923
- void (*task)(void *), void *arg,
924
- void (*on_finish)(void *)) {
925
- validate_mem();
926
- if (task == NULL)
927
- return -1;
928
- timer_protocol_s *protocol = NULL;
929
- intptr_t uuid = -1;
930
- int fd = reactor_make_timer();
931
- if (fd == -1) {
932
- // perror("couldn't create a timer fd");
933
- goto error;
934
- }
935
- uuid = sock_open(fd);
936
- if (uuid == -1)
937
- goto error;
938
- clear_uuid(uuid);
939
- protocol = timer_alloc(task, arg, milliseconds, repetitions, on_finish);
940
- if (protocol == NULL)
941
- goto error;
942
- protocol_fd(fd) = (protocol_s *)protocol;
943
- if (server_data.running && reactor_add_timer(uuid, milliseconds))
944
- goto error;
945
- return 0;
946
- error:
947
- if (uuid != -1)
948
- sock_close(uuid);
949
- else if (fd != -1)
950
- close(fd);
951
-
952
- if (protocol != NULL) {
953
- protocol_fd(fd) = NULL;
954
- timer_on_close((protocol_s *)protocol);
955
- }
956
- return -1;
957
- }