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
@@ -0,0 +1,423 @@
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 H_FACIL_H
8
+ /**
9
+ "facil.h" is the main header for the facil.io server platform.
10
+ */
11
+ #ifdef __cplusplus
12
+ extern "C" {
13
+ #endif
14
+
15
+ #define H_FACIL_H
16
+ #define FACIL_VERSION_MAJOR 0
17
+ #define FACIL_VERSION_MINOR 4
18
+ #define FACIL_VERSION_PATCH 0
19
+
20
+ #ifndef FACIL_PRINT_STATE
21
+ /**
22
+ When FACIL_PRINT_STATE is set to 1, facil.io will print out common messages
23
+ regarding the server state (start / finish / listen messages).
24
+ */
25
+ #define FACIL_PRINT_STATE 1
26
+ #endif
27
+ /* *****************************************************************************
28
+ Required facil libraries
29
+ ***************************************************************************** */
30
+ #include "defer.h"
31
+ #include "sock.h"
32
+
33
+ /* *****************************************************************************
34
+ Core object types
35
+ ***************************************************************************** */
36
+
37
+ typedef struct FacilIOProtocol protocol_s;
38
+ /**************************************************************************/ /**
39
+ * The Protocol
40
+
41
+ The Protocol struct defines the callbacks used for the connection and sets it's
42
+ behaviour. The Protocol struct is part of facil.io's core design.
43
+
44
+ For concurrency reasons, a protocol instance SHOULD be unique to each
45
+ connections. Different connections shouldn't share a single protocol object
46
+ (callbacks and data can obviously be shared).
47
+
48
+ All the callbacks recieve a unique connection ID (a localized UUID) that can be
49
+ converted to the original file descriptor when in need.
50
+
51
+ This allows facil.io to prevent old connection handles from sending data
52
+ to new connections after a file descriptor is "recycled" by the OS.
53
+ */
54
+ struct FacilIOProtocol {
55
+ /**
56
+ * A string to identify the protocol's service (i.e. "http").
57
+ *
58
+ * The string should be a global constant, only a pointer comparison will be
59
+ * used (not `strcmp`).
60
+ */
61
+ const char *service;
62
+ /** called when a data is available, but will not run concurrently */
63
+ void (*on_data)(intptr_t uuid, protocol_s *protocol);
64
+ /** called when the socket is ready to be written to. */
65
+ void (*on_ready)(intptr_t uuid, protocol_s *protocol);
66
+ /** called when the server is shutting down,
67
+ * but before closing the connection. */
68
+ void (*on_shutdown)(intptr_t uuid, protocol_s *protocol);
69
+ /** called when the connection was closed, but will not run concurrently */
70
+ void (*on_close)(protocol_s *protocol);
71
+ /** called when a connection's timeout was reached */
72
+ void (*ping)(intptr_t uuid, protocol_s *protocol);
73
+ /** private metadata used by facil. */
74
+ size_t rsv;
75
+ };
76
+
77
+ /**************************************************************************/ /**
78
+ * Listening to Incoming Connections
79
+
80
+ Listenning to incoming connections is pretty straight forward.
81
+
82
+ After a new connection is accepted, the `on_open` callback is called. `on_open`
83
+ should allocate the new connection's protocol and retuen it's address.
84
+
85
+ The protocol's `on_close` callback is expected to handle the cleanup.
86
+
87
+ These settings will be used to setup listening sockets.
88
+
89
+ i.e.
90
+
91
+ ```c
92
+ // A callback to be called whenever data is available on the socket
93
+ static void echo_on_data(intptr_t uuid,
94
+ protocol_s *prt
95
+ ) {
96
+ (void)prt; // we can ignore the unused argument
97
+ // echo buffer
98
+ char buffer[1024] = {'E', 'c', 'h', 'o', ':', ' '};
99
+ ssize_t len;
100
+ // Read to the buffer, starting after the "Echo: "
101
+ while ((len = sock_read(uuid, buffer + 6, 1018)) > 0) {
102
+ // Write back the message
103
+ sock_write(uuid, buffer, len + 6);
104
+ // Handle goodbye
105
+ if ((buffer[6] | 32) == 'b' && (buffer[7] | 32) == 'y' &&
106
+ (buffer[8] | 32) == 'e') {
107
+ sock_write(uuid, "Goodbye.\n", 9);
108
+ sock_close(uuid);
109
+ return;
110
+ }
111
+ }
112
+ }
113
+
114
+ // A callback called whenever a timeout is reach
115
+ static void echo_ping(intptr_t uuid, protocol_s *prt) {
116
+ (void)prt; // we can ignore the unused argument
117
+ sock_write(uuid, "Server: Are you there?\n", 23);
118
+ }
119
+
120
+ // A callback called if the server is shutting down...
121
+ // ... while the connection is still open
122
+ static void echo_on_shutdown(intptr_t uuid, protocol_s *prt) {
123
+ (void)prt; // we can ignore the unused argument
124
+ sock_write(uuid, "Echo server shutting down\nGoodbye.\n", 35);
125
+ }
126
+
127
+ // A callback called for new connections
128
+ static protocol_s *echo_on_open(intptr_t uuid, void *udata) {
129
+ (void)udata; // ignore this
130
+ // Protocol objects MUST always be dynamically allocated.
131
+ protocol_s *echo_proto = malloc(sizeof(*echo_proto));
132
+ *echo_proto = (protocol_s){
133
+ .service = "echo",
134
+ .on_data = echo_on_data,
135
+ .on_shutdown = echo_on_shutdown,
136
+ .on_close = (void (*)(protocol_s *))free, // simply free when done
137
+ .ping = echo_ping};
138
+
139
+ sock_write(uuid, "Echo Service: Welcome\n", 22);
140
+ facil_set_timeout(uuid, 5);
141
+ return echo_proto;
142
+ }
143
+
144
+ int main() {
145
+ // Setup a listening socket
146
+ if (facil_listen(.port = "8888", .on_open = echo_on_open))
147
+ perror("No listening socket available on port 8888"), exit(-1);
148
+ // Run the server and hang until a stop signal is received.
149
+ facil_run(.threads = 4, .processes = 1);
150
+ }
151
+
152
+ ```
153
+ */
154
+ struct facil_listen_args {
155
+ /**
156
+ * Called whenever a new connection is accepted.
157
+ * Should return a pointer to
158
+ * the connection's protocol. */
159
+ protocol_s *(*on_open)(intptr_t fduuid, void *udata);
160
+ /** The network service / port. Defaults to "3000". */
161
+ const char *port;
162
+ /** The socket binding address. Defaults to the recommended NULL. */
163
+ const char *address;
164
+ /** Opaque user data. */
165
+ void *udata;
166
+ /**
167
+ * Called when the server starts, allowing for further initialization, such as
168
+ * timed event scheduling.
169
+ *
170
+ * This will be called seperately for every process. */
171
+ void (*on_start)(void *udata);
172
+ /**
173
+ * Called when the server is done, usable for cleanup.
174
+ *
175
+ * This will be called seperately for every process. */
176
+ void (*on_finish)(void *udata);
177
+ };
178
+
179
+ /** Schedule a network service on a listening socket. */
180
+ int facil_listen(struct facil_listen_args args);
181
+
182
+ /**
183
+ * Schedule a network service on a listening socket.
184
+ *
185
+ * See the `struct facil_listen_args` details for any possible named arguments.
186
+ */
187
+ #define facil_listen(...) facil_listen((struct facil_listen_args){__VA_ARGS__})
188
+
189
+ /* *****************************************************************************
190
+ Connecting to remote servers as a client
191
+ ***************************************************************************** */
192
+
193
+ /**
194
+ Named arguments for the `server_connect` function, that allows non-blocking
195
+ connections to be established.
196
+ */
197
+ struct facil_connect_args {
198
+ /** The address of the server we are connecting to. */
199
+ char *address;
200
+ /** The port on the server we are connecting to. */
201
+ char *port;
202
+ /**
203
+ * The `on_connect` callback should return a pointer to a protocol object
204
+ * that will handle any connection related events.
205
+ */
206
+ protocol_s *(*on_connect)(intptr_t uuid, void *udata);
207
+ /**
208
+ * The `on_fail` is called when a socket fails to connect.
209
+ */
210
+ void (*on_fail)(void *udata);
211
+ /** Opaque user data. */
212
+ void *udata;
213
+ };
214
+
215
+ /**
216
+ Creates a client connection (in addition or instead of the server).
217
+
218
+ See the `struct facil_listen_args` details for any possible named arguments.
219
+
220
+ * `.address` should be the address of the server.
221
+
222
+ * `.port` the server's port.
223
+
224
+ * `.udata`opaque user data.
225
+
226
+ * `.on_connect` called once a connection was established.
227
+
228
+ Should return a pointer to a `protocol_s` object, to handle connection
229
+ callbacks.
230
+
231
+ * `.on_fail` called if a connection failed to establish.
232
+
233
+ (experimental: untested)
234
+ */
235
+ intptr_t facil_connect(struct facil_connect_args);
236
+ #define facil_connect(...) \
237
+ facil_connect((struct facil_connect_args){__VA_ARGS__})
238
+
239
+ /* *****************************************************************************
240
+ Core API
241
+ ***************************************************************************** */
242
+
243
+ struct facil_run_args {
244
+ /** The number of threads to run in the thread pool. Has "smart" defaults. */
245
+ uint16_t threads;
246
+ /** The number of processes to run (including this one). "smart" defaults. */
247
+ uint16_t processes;
248
+ /** called if the event loop in cycled with no pending events. */
249
+ void (*on_idle)(void);
250
+ /** called when the server is done, to clean up any leftovers. */
251
+ void (*on_finish)(void);
252
+ };
253
+ /**
254
+ * Starts the facil.io event loop. This function will return after facil.io is
255
+ * done (after shutdown).
256
+ *
257
+ * See the `struct facil_run_args` details for any possible named arguments.
258
+ */
259
+ void facil_run(struct facil_run_args args);
260
+ #define facil_run(...) facil_run((struct facil_run_args){__VA_ARGS__})
261
+ /**
262
+ * Attaches (or updates) a protocol object to a socket UUID.
263
+ *
264
+ * The new protocol object can be NULL, which will practically "hijack" the
265
+ * socket away from facil.
266
+ *
267
+ * The old protocol's `on_close` will be scheduled, if they both exist.
268
+ *
269
+ * Returns -1 on error and 0 on success.
270
+ */
271
+ int facil_attach(intptr_t uuid, protocol_s *protocol);
272
+
273
+ /** Sets a timeout for a specific connection (if active). */
274
+ void facil_set_timeout(intptr_t uuid, uint8_t timeout);
275
+
276
+ /** Gets a timeout for a specific connection. Returns 0 if none. */
277
+ uint8_t facil_get_timeout(intptr_t uuid);
278
+
279
+ /* *****************************************************************************
280
+ Helper API
281
+ ***************************************************************************** */
282
+
283
+ /**
284
+ Returns the last time the server reviewed any pending IO events.
285
+ */
286
+ time_t facil_last_tick(void);
287
+
288
+ /** Counts all the connections of a specific type `service`. */
289
+ size_t facil_count(void *service);
290
+
291
+ /**
292
+ * Creates a system timer (at the cost of 1 file descriptor).
293
+ *
294
+ * The task will repeat `repetitions` times. If `repetitions` is set to 0, task
295
+ * will repeat forever.
296
+ *
297
+ * Returns -1 on error or the new file descriptor on succeess.
298
+ */
299
+ int facil_run_every(size_t milliseconds, size_t repetitions,
300
+ void (*task)(void *), void *arg, void (*on_finish)(void *));
301
+
302
+ /**
303
+ * This is used to lock the protocol againste concurrency collisions and
304
+ * concurent memory deallocation.
305
+ *
306
+ * However, there are three levels of protection that allow non-coliding tasks
307
+ * to protect the protocol object from being deallocated while in use:
308
+ *
309
+ * * `FIO_PR_LOCK_TASK` - a task lock locks might change data owned by the
310
+ * protocol object. This task is used for tasks such as `on_data` and
311
+ * (usually) `facil_defer`.
312
+ *
313
+ * * `FIO_PR_LOCK_WRITE` - a lock that promises only to use static data (data
314
+ * that tasks never changes) in order to write to the underlying socket.
315
+ * This lock is used for tasks such as `on_ready` and `ping`
316
+ *
317
+ * * `FIO_PR_LOCK_STATE` - a lock that promises only to retrive static data
318
+ * (data that tasks never changes), performing no actions. This usually
319
+ * isn't used for client side code (used internally by facil) and is only
320
+ * meant for very short locks.
321
+ */
322
+ enum facil_protocol_lock_e {
323
+ FIO_PR_LOCK_TASK = 0,
324
+ FIO_PR_LOCK_WRITE = 1,
325
+ FIO_PR_LOCK_STATE = 2,
326
+ };
327
+
328
+ /** Named arguments for the `facil_defer` function. */
329
+ struct facil_defer_args_s {
330
+ /** The socket (UUID) that will perform the task. This is required.*/
331
+ intptr_t uuid;
332
+ /** The type of task to be performed. Defaults to `FIO_PR_LOCK_TASK` but could
333
+ * also be seto to `FIO_PR_LOCK_WRITE`. */
334
+ enum facil_protocol_lock_e task_type;
335
+ /** The task (function) to be performed. This is required. */
336
+ void (*task)(intptr_t uuid, protocol_s *, void *arg);
337
+ /** An opaque user data that will be passed along to the task. */
338
+ void *arg;
339
+ /** A fallback task, in case the connection was lost. Good for cleanup. */
340
+ void (*fallback)(intptr_t uuid, void *arg);
341
+ };
342
+ /**
343
+ * Schedules a protected connection task. The task will run within the
344
+ * connection's lock.
345
+ *
346
+ * If an error ocuurs or the connection is closed before the task can run, the
347
+ * `fallback` task wil be called instead, allowing for resource cleanup.
348
+ */
349
+ void facil_defer(struct facil_defer_args_s args);
350
+ #define facil_defer(...) facil_defer((struct facil_defer_args_s){__VA_ARGS__})
351
+
352
+ /** Named arguments for the `facil_defer` function. */
353
+ struct facil_each_args_s {
354
+ /** The socket (UUID) that originates the task or -1 if none (0 is a valid
355
+ * UUID). This socket will be EXCLUDED from performing the task.*/
356
+ intptr_t origin;
357
+ /** The target type of protocol that should perform the task. This is
358
+ * required. */
359
+ const void *service;
360
+ /** The type of task to be performed. Defaults to `FIO_PR_LOCK_TASK` but could
361
+ * also be seto to `FIO_PR_LOCK_WRITE`. */
362
+ enum facil_protocol_lock_e task_type;
363
+ /** The task (function) to be performed. This is required. */
364
+ void (*task)(intptr_t uuid, protocol_s *, void *arg);
365
+ /** An opaque user data that will be passed along to the task. */
366
+ void *arg;
367
+ /** An on_complete callback. Good for cleanup. */
368
+ void (*on_complete)(intptr_t uuid, void *arg);
369
+ };
370
+
371
+ /**
372
+ * Schedules a protected connection task for each `service` connection.
373
+ * The tasks will run within each of the connection's locks.
374
+ *
375
+ * Once all the tasks were performed, the `on_complete` callback will be called.
376
+ *
377
+ * Returns -1 on error. `on_complete` is always called (even on error).
378
+ */
379
+ int facil_each(struct facil_each_args_s args);
380
+ #define facil_each(...) facil_each((struct facil_each_args_s){__VA_ARGS__})
381
+
382
+ /* *****************************************************************************
383
+ Lower Level API - for special circumstances, use with care under .
384
+ ***************************************************************************** */
385
+
386
+ /**
387
+ * This function allows out-of-task access to a connection's `protocol_s` object
388
+ * by attempting to lock it.
389
+ *
390
+ * CAREFUL: mostly, the protocol object will be locked and a pointer will be
391
+ * sent to the connection event's callback. However, if you need access to the
392
+ * protocol object from outside a running connection task, you might need to
393
+ * lock the protocol to prevent it from being closed in the background.
394
+ *
395
+ * facil.io uses three different locks:
396
+ *
397
+ * * FIO_PR_LOCK_TASK locks the protocol from normal tasks (i.e. `on_data`,
398
+ * `facil_defer`, `facil_every`).
399
+ *
400
+ * * FIO_PR_LOCK_WRITE locks the protocol for high priority `sock_write`
401
+ * oriented tasks (i.e. `ping`, `on_ready`).
402
+ *
403
+ * * FIO_PR_LOCK_STATE locks the protocol for quick operations that need to copy
404
+ * data from the protoccol's data stracture.
405
+ *
406
+ * IMPORTANT: Remember to call `facil_protocol_unlock` using the same lock type.
407
+ *
408
+ * Returns NULL on error (lock busy == EWOULDBLOCK, connection invalid == EBADF)
409
+ * and a pointer to a protocol object on success.
410
+ *
411
+ * On error, consider calling `facil_defer` or `defer` instead of busy waiting.
412
+ * Busy waiting SHOULD be avoided whenever possible.
413
+ */
414
+ protocol_s *facil_protocol_try_lock(intptr_t uuid, enum facil_protocol_lock_e);
415
+ /** Don't unlock what you don't own... see `facil_protocol_try_lock` for
416
+ * details. */
417
+ void facil_protocol_unlock(protocol_s *pr, enum facil_protocol_lock_e);
418
+
419
+ #ifdef __cplusplus
420
+ } /* extern "C" */
421
+ #endif
422
+
423
+ #endif /* H_FACIL_H */
@@ -4,7 +4,97 @@ license: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
+ #define _GNU_SOURCE
8
+
7
9
  #include "http.h"
10
+ #include "http1.h"
11
+
12
+ #include <string.h>
13
+ #include <strings.h>
14
+ #include <time.h>
15
+ /* *****************************************************************************
16
+ The global HTTP protocol
17
+ ***************************************************************************** */
18
+
19
+ /**
20
+ Return the callback used for creating the correct `settings` HTTP protocol.
21
+ */
22
+ http_on_open_func http_get_on_open_func(http_settings_s *settings) {
23
+ static void *route[2] = {(void *)http1_on_open};
24
+ return (http_on_open_func)route[settings->version];
25
+ }
26
+ /**
27
+ Return the callback used for freeing the HTTP protocol in the `settings`.
28
+ */
29
+ http_on_finish_func http_get_on_finish_func(http_settings_s *settings) {
30
+ static void *route[2] = {NULL};
31
+ return (http_on_finish_func)route[settings->version];
32
+ }
33
+
34
+ void http_on_finish(void *set) {
35
+ http_settings_s *settings = set;
36
+ if (http_get_on_finish_func(set))
37
+ http_get_on_finish_func(set)(set);
38
+ if (settings->private_metaflags & 1)
39
+ free((void *)settings->public_folder);
40
+ if (settings->private_metaflags & 2)
41
+ free(settings);
42
+ }
43
+
44
+ /**
45
+ Listens for incoming HTTP connections on the specified posrt and address,
46
+ implementing the requested settings.
47
+
48
+ Since facil.io doesn't support native TLS/SLL
49
+ */
50
+ #undef http_listen
51
+ int http_listen(const char *port, const char *address,
52
+ http_settings_s arg_settings) {
53
+ if (arg_settings.on_request == NULL) {
54
+ fprintf(
55
+ stderr,
56
+ "ERROR: http_listen requires the .on_request parameter to be set\n");
57
+ exit(11);
58
+ }
59
+ http_on_open_func on_open_callback = http_get_on_open_func(&arg_settings);
60
+ if (!on_open_callback) {
61
+ fprintf(stderr, "ERROR: The requested HTTP protocol version isn't "
62
+ "supported at the moment.\n");
63
+ exit(11);
64
+ }
65
+ http_settings_s *settings = malloc(sizeof(*settings));
66
+ *settings = arg_settings;
67
+ settings->private_metaflags = 2;
68
+ if (!settings->max_body_size)
69
+ settings->max_body_size = HTTP_DEFAULT_BODY_LIMIT;
70
+ if (!settings->timeout)
71
+ settings->timeout = 5;
72
+ if (settings->public_folder) {
73
+ settings->public_folder_length = strlen(settings->public_folder);
74
+ if (settings->public_folder[0] == '~' &&
75
+ settings->public_folder[1] == '/' && getenv("HOME")) {
76
+ char *home = getenv("HOME");
77
+ size_t home_len = strlen(home);
78
+ char *tmp = malloc(settings->public_folder_length + home_len + 1);
79
+ memcpy(tmp, home, home_len);
80
+ if (home[home_len - 1] == '/')
81
+ --home_len;
82
+ memcpy(tmp + home_len, settings->public_folder + 1,
83
+ settings->public_folder_length); // copy also the NULL
84
+ settings->public_folder = tmp;
85
+ settings->private_metaflags |= 1;
86
+ settings->public_folder_length = strlen(settings->public_folder);
87
+ }
88
+ }
89
+
90
+ return facil_listen(.port = port, .address = address,
91
+ .on_finish = http_on_finish, .on_open = on_open_callback,
92
+ .udata = settings);
93
+ }
94
+
95
+ /* *****************************************************************************
96
+ HTTP helpers.
97
+ ***************************************************************************** */
8
98
 
9
99
  /**
10
100
  A faster (yet less localized) alternative to `gmtime_r`.