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
@@ -0,0 +1,435 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef LIB_SERVER
8
+ #define LIB_SERVER "0.4.0"
9
+
10
+ #ifndef _GNU_SOURCE
11
+ #define _GNU_SOURCE
12
+ #endif
13
+
14
+ #include <stdint.h>
15
+ #include <unistd.h>
16
+ #include <stdio.h>
17
+ #include <stdlib.h>
18
+
19
+ /* lib server is based off and requires the following libraries: */
20
+ #include "libreact.h"
21
+ #include "libasync.h"
22
+ #include "libsock.h"
23
+ #include "spnlock.h"
24
+
25
+ #ifndef SERVER_PRINT_STATE
26
+ /**
27
+ When SERVER_PRINT_STATE is set to 1, the server API will print out common
28
+ messages regarding the server state (start / finish / listen messages).
29
+ */
30
+ #define SERVER_PRINT_STATE 1
31
+ #endif
32
+
33
+ #if LIB_ASYNC_VERSION_MINOR != 4 || LIB_REACT_VERSION_MINOR != 3 || \
34
+ LIB_SOCK_VERSION_MINOR != 2
35
+ #warning Lib-Server dependency versions are not in sync. Please review API versions.
36
+ #endif
37
+
38
+ #ifndef __unused
39
+ #define __unused __attribute__((unused))
40
+ #endif
41
+
42
+ /** \file
43
+ ## LibServer - a dynamic protocol network services library
44
+
45
+ * Library (not a framework): meanning, closer to the metal, abstracting only
46
+ what is required for API simplicity, error protection and performance.
47
+
48
+ * Dynamic Protocol: meanning a service can change protocols mid-stream. Example
49
+ usecase: Websockets (HTTP Upgrade).
50
+
51
+ * Network services: meanning multiple listenning network ports, each with it's
52
+ own logic.
53
+
54
+ `libserver` utilizes `libreact`, `libasync` and `libsock` to create a simple
55
+ API wrapper around these minimalistic libraries and managing the "glue" that
56
+ makes them work together.
57
+
58
+ It's simple and it's awesome :-)
59
+
60
+ Here's a simple example that emulates an HTTP hello world server. This example
61
+ will count the number of client and messages using the server and demonstrates
62
+ some recommended implementation techniques, such as protocol inheritance.
63
+
64
+ #include "libserver.h" // the reactor library
65
+ #include <stdatomic.h> // We'll use atomics to safely count client events
66
+
67
+ #define THREADS 4
68
+ #define PROCESSES 4
69
+
70
+ void on_close(protocol_s* protocol);
71
+ void on_shutdown(intptr_t sock, protocol_s* protocol);
72
+ void on_data(intptr_t sock, protocol_s* protocol);
73
+ protocol_s* demo_on_open(intptr_t fd, void* udata);
74
+
75
+ // Our demo protocol object uses "C" style inheritance,
76
+ // where pointers location are the same so that a simple cast
77
+ // from one object to the other, allows us to access more data.
78
+ struct DemoProtocol {
79
+ protocol_s protocol; // must be first for C style inheritance
80
+ size_t _Atomic opened;
81
+ size_t _Atomic closed;
82
+ size_t _Atomic shutdown;
83
+ size_t _Atomic messages;
84
+ } demo_protocol = {
85
+ .protocol.service = "Demo", // This allows us to ID the protocol type.
86
+ .protocol.on_data = on_data,
87
+ .protocol.on_shutdown = on_shutdown,
88
+ .protocol.on_close = on_close,
89
+ };
90
+
91
+ // type-casting helper
92
+ #define pr2demo(protocol) ((struct DemoProtocol*)(protocol))
93
+
94
+ // A simple Hello World HTTP response emulation
95
+ char hello_message[] =
96
+ "HTTP/1.1 200 OK\r\n"
97
+ "Content-Length: 12\r\n"
98
+ "Connection: keep-alive\r\n"
99
+ "Keep-Alive: 1;timeout=5\r\n"
100
+ "\r\n"
101
+ "Hello World!";
102
+
103
+ protocol_s* on_open(intptr_t fd, void* udata) {
104
+ // Count events
105
+ atomic_fetch_add(&demo_protocol.opened, 1);
106
+ // Set timeout
107
+ server_set_timeout(fd, 5);
108
+ // * return pointer to the protocol.
109
+ // * This is the same as `(protocol_s *)&demo_protocol`
110
+ return &demo_protocol.protocol;
111
+ }
112
+
113
+ void on_data(intptr_t sock, protocol_s* protocol) {
114
+ // read data
115
+ char data[1024];
116
+ if (sock_read(sock, data, 1024)) {
117
+ // Count event
118
+ atomic_fetch_add(&pr2demo(protocol)->messages, 1);
119
+ // send reply
120
+ sock_write(sock, hello_message, sizeof(hello_message) - 1);
121
+ }
122
+ }
123
+
124
+ void on_close(protocol_s* protocol) {
125
+ // Count event
126
+ atomic_fetch_add(&pr2demo(protocol)->closed, 1);
127
+ }
128
+
129
+ void on_shutdown(intptr_t sock, protocol_s* protocol) {
130
+ // Count event
131
+ atomic_fetch_add(&pr2demo(protocol)->shutdown, 1);
132
+ }
133
+
134
+ void on_idle(void) {
135
+ // idle event example
136
+ fprintf(stderr, "Server was idle, with %lu connections.\n",
137
+ server_count(NULL));
138
+ }
139
+
140
+ int main() {
141
+ // this isn't required normally,
142
+ // but some systems require atomics to be initialized.
143
+ atomic_store(&demo_protocol.opened, 0);
144
+ atomic_store(&demo_protocol.closed, 0);
145
+ atomic_store(&demo_protocol.shutdown, 0);
146
+ atomic_store(&demo_protocol.messages, 0);
147
+ // run the server
148
+ server_listen(.port = "3000", .on_open = on_open, .udata = NULL);
149
+ server_run(.threads = THREADS, .processes = PROCESSES,
150
+ .on_idle = on_idle);
151
+ // print results
152
+ fprintf(stderr,
153
+ "** Server returned\n"
154
+ "** %lu clients connected.\n"
155
+ "** %lu clients disconnected.\n"
156
+ "** %lu clients were connected when shutdown was called.\n"
157
+ "** %lu messages were sent\n",
158
+ atomic_load(&demo_protocol.opened),
159
+ atomic_load(&demo_protocol.closed),
160
+ atomic_load(&demo_protocol.shutdown),
161
+ atomic_load(&demo_protocol.messages));
162
+ }
163
+
164
+
165
+ */
166
+
167
+ /**************************************************************************/ /**
168
+ * General info
169
+ */
170
+
171
+ /* The following types are defined for the userspace of this library: */
172
+
173
+ // struct Server; /** used internally. no public data exposed */
174
+ struct ServerSettings; /** sets up the server's behavior */
175
+ struct ServerServiceSettings; /** sets up a listenning socket's behavior */
176
+ typedef struct Protocol protocol_s; /** controls connection events */
177
+
178
+ /**************************************************************************/ /**
179
+ * The Protocol
180
+
181
+ The Protocol struct defines the callbacks used for the connection and sets the
182
+ behaviour for the connection's protocol.
183
+
184
+ All the callbacks recieve a unique connection ID (a semi UUID) that can be
185
+ converted to the original file descriptor if in need.
186
+
187
+ This allows the Server API to prevent old connection handles from sending data
188
+ to new connections after a file descriptor is "recycled" by the OS.
189
+ */
190
+ struct Protocol {
191
+ /**
192
+ * A string to identify the protocol's service (i.e. "http").
193
+ *
194
+ * The string should be a global constant, only a pointer comparison will be
195
+ * made (not `strcmp`).
196
+ */
197
+ const char* service;
198
+ /** called when a data is available, but will not run concurrently */
199
+ void (*on_data)(intptr_t fduuid, protocol_s* protocol);
200
+ /** called when the socket is ready to be written to. */
201
+ void (*on_ready)(intptr_t fduuid, protocol_s* protocol);
202
+ /** called when the server is shutting down,
203
+ * but before closing the connection. */
204
+ void (*on_shutdown)(intptr_t fduuid, protocol_s* protocol);
205
+ /** called when the connection was closed, but will not run concurrently */
206
+ void (*on_close)(protocol_s* protocol);
207
+ /** called when a connection's timeout was reached */
208
+ void (*ping)(intptr_t fduuid, protocol_s* protocol);
209
+ /** private metadata used for object protection */
210
+ spn_lock_i callback_lock;
211
+ };
212
+
213
+ /**************************************************************************/ /**
214
+ * The Service Settings
215
+
216
+ These settings will be used to setup listenning sockets.
217
+ */
218
+ struct ServerServiceSettings {
219
+ /** Called whenever a new connection is accepted. Should return a pointer to
220
+ * the connection's protocol. */
221
+ protocol_s* (*on_open)(intptr_t fduuid, void* udata);
222
+ /** The network service / port. Defaults to "3000". */
223
+ const char* port;
224
+ /** The socket binding address. Defaults to the recommended NULL. */
225
+ const char* address;
226
+ /** Opaque user data. */
227
+ void* udata;
228
+ /**
229
+ * Called when the server starts, allowing for further initialization, such as
230
+ * timed event scheduling.
231
+ *
232
+ * This will be called seperately for every process. */
233
+ void (*on_start)(void* udata);
234
+ /** called when the server is done, to clean up any leftovers. */
235
+ void (*on_finish)(void* udata);
236
+ };
237
+
238
+ /**************************************************************************/ /**
239
+ * The Server Settings
240
+
241
+ These settings will be used to setup server behaviour. missing settings will be
242
+ filled in with default values.
243
+ */
244
+ struct ServerSettings {
245
+ /** called if the event loop in cycled with no pending events. */
246
+ void (*on_idle)(void);
247
+ /**
248
+ * Called when the server starts, allowing for further initialization, such as
249
+ * timed event scheduling.
250
+ *
251
+ * This will be called seperately for every process. */
252
+ void (*on_init)(void);
253
+ /** called when the server is done, to clean up any leftovers. */
254
+ void (*on_finish)(void);
255
+ /**
256
+ Sets the amount of threads to be created for the server's thread-pool.
257
+ Defaults to 1 - the reactor and all callbacks will work using a single working
258
+ thread, allowing for an evented single threaded design.
259
+ */
260
+ size_t threads;
261
+ /** Sets the amount of processes to be used (processes will be forked).
262
+ Defaults to 1 working processes (no forking). */
263
+ size_t processes;
264
+ };
265
+
266
+ /* *****************************************************************************
267
+ * The Server API
268
+ * (and helper functions)
269
+ */
270
+
271
+ /* *****************************************************************************
272
+ * Server actions
273
+ */
274
+
275
+ /**
276
+ Listens to a server with any of the following server settings:
277
+
278
+ * `.threads` the number of threads to initiate in the server's thread pool.
279
+
280
+ * `.processes` the number of processes to use (a value of more then 1 will
281
+ initiate a `fork`).
282
+
283
+ * `.on_init` an on initialization callback (for every process forked).
284
+ `void (*callback)(void);``
285
+
286
+ * `.on_finish` a post run callback (for every process forked).
287
+ `void (*callback)(void);``
288
+
289
+ * `.on_idle` an idle server callback (for every process forked).
290
+ `void (*callback)(void);``
291
+
292
+ This method blocks the current thread until the server is stopped when a
293
+ SIGINT/SIGTERM is received.
294
+
295
+ To kill the server use the `kill` function with a SIGINT.
296
+ */
297
+ int server_listen(struct ServerServiceSettings);
298
+ #define server_listen(...) \
299
+ server_listen((struct ServerServiceSettings){__VA_ARGS__})
300
+ /** runs the server, hanging the current process and thread. */
301
+ ssize_t server_run(struct ServerSettings);
302
+ #define server_run(...) server_run((struct ServerSettings){__VA_ARGS__})
303
+ /** Stops the server, shouldn't be called unless int's impossible to send an
304
+ * INTR signal. */
305
+ void server_stop(void);
306
+ /**
307
+ Returns the last time the server reviewed any pending IO events.
308
+ */
309
+ time_t server_last_tick(void);
310
+ /****************************************************************************
311
+ * Socket actions
312
+ */
313
+
314
+ /**
315
+ Gets the active protocol object for the requested file descriptor.
316
+
317
+ Returns NULL on error (i.e. connection closed), otherwise returns a `protocol_s`
318
+ pointer.
319
+ */
320
+ protocol_s* server_get_protocol(intptr_t uuid);
321
+ /**
322
+ Sets a new active protocol object for the requested file descriptor.
323
+
324
+ This also schedules the old protocol's `on_close` callback to run, making sure
325
+ all resources are released.
326
+
327
+ Returns -1 on error (i.e. connection closed), otherwise returns 0.
328
+ */
329
+ ssize_t server_switch_protocol(intptr_t fd, protocol_s* new_protocol);
330
+ /**
331
+ Sets a connection's timeout.
332
+
333
+ Returns -1 on error (i.e. connection closed), otherwise returns 0.
334
+ */
335
+ void server_set_timeout(intptr_t uuid, uint8_t timeout);
336
+
337
+ /** Attaches an existing connection (fd) to the server's reactor and protocol
338
+ management system, so that the server can be used also to manage connection
339
+ based resources asynchronously (i.e. database resources etc').
340
+
341
+ On failure the fduuid_u.data.fd value will be -1.
342
+ */
343
+ intptr_t server_attach(int fd, protocol_s* protocol);
344
+ /** Hijack a socket (file descriptor) from the server, clearing up it's
345
+ resources and calling the protocol's `on_close` callback (making sure allocated
346
+ resources are freed).
347
+
348
+ The control of the socket is totally relinquished.
349
+
350
+ This method will block until all the data in the buffer is sent before
351
+ releasing control of the socket.
352
+
353
+ The returned value is the fd for the socket, or -1 on error.
354
+ */
355
+ int server_hijack(intptr_t uuid);
356
+ /** Counts the number of connections for the specified protocol (NULL = all
357
+ protocols). */
358
+ long server_count(char* service);
359
+
360
+ /****************************************************************************
361
+ * Read and Write
362
+ *
363
+ * Simpley use `libsock` API for read/write.
364
+ */
365
+
366
+ /****************************************************************************
367
+ * Tasks + Async
368
+ */
369
+
370
+ /**
371
+ Schedules a specific task to run asyncronously for each connection (except the
372
+ origin connection).
373
+ a NULL service identifier == all connections (all protocols).
374
+
375
+ The task is performed within each target connection's busy "lock", meanning no
376
+ two tasks (or `on_data` events) should be performed at the same time
377
+ (concurrency will be avoided within the context of each connection, except for
378
+ `on_shutdown`, `on_close` and `ping`).
379
+
380
+ The `on_finish` callback will be called once the task is finished and it will
381
+ receive the originating connection's UUID (could be 0). The originating
382
+ connection might have been closed by that time.
383
+
384
+ The `service` string (pointer) identifier MUST be a constant string object OR
385
+ a string that will persist until the `on_finish` callback is called. In other
386
+ words, either hardcode the string or use `malloc` to allocate it before
387
+ calling `each` and `free` to release the string from within the `on_finish`
388
+ callback.
389
+
390
+ It is recommended the `on_finish` callback is only used to perform any
391
+ resource cleanup necessary.
392
+ */
393
+ void server_each(intptr_t origin_uuid,
394
+ const char* service,
395
+ void (*task)(intptr_t uuid, protocol_s* protocol, void* arg),
396
+ void* arg,
397
+ void (*on_finish)(intptr_t origin_uuid,
398
+ protocol_s* protocol,
399
+ void* arg));
400
+ /** Schedules a specific task to run asyncronously for a specific connection.
401
+
402
+ returns -1 on failure, 0 on success (success being scheduling the task).
403
+
404
+ If a connection was terminated before performing their sceduled tasks, the
405
+ `fallback` task will be performed instead.
406
+
407
+ It is recommended to perform any resource cleanup within the fallback function
408
+ and call the fallback function from within the main task, but other designes
409
+ are valid as well.
410
+ */
411
+ void server_task(intptr_t uuid,
412
+ void (*task)(intptr_t uuid, protocol_s* protocol, void* arg),
413
+ void* arg,
414
+ void (*fallback)(intptr_t uuid, void* arg));
415
+ /** Creates a system timer (at the cost of 1 file descriptor) and pushes the
416
+ timer to the reactor. The task will repeat `repetitions` times. if
417
+ `repetitions` is set to 0, task will repeat forever. Returns -1 on error
418
+ or the new file descriptor on succeess.
419
+ */
420
+ int server_run_every(size_t milliseconds,
421
+ size_t repetitions,
422
+ void (*task)(void*),
423
+ void* arg,
424
+ void (*on_finish)(void*));
425
+
426
+ /** Creates a system timer (at the cost of 1 file descriptor) and pushes the
427
+ timer to the reactor. The task will NOT repeat. Returns -1 on error or the
428
+ new file descriptor on succeess. */
429
+ __unused static inline int server_run_after(size_t milliseconds,
430
+ void task(void*),
431
+ void* arg) {
432
+ return server_run_every(milliseconds, 1, task, arg, NULL);
433
+ }
434
+
435
+ #endif
@@ -0,0 +1,950 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
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
+
11
+ #include "libsock.h"
12
+
13
+ #include <string.h>
14
+ #include <stdio.h>
15
+ #include <time.h>
16
+ #include <fcntl.h>
17
+ #include <errno.h>
18
+ #include <netdb.h>
19
+ #include <sys/types.h>
20
+ #include <sys/socket.h>
21
+ #include <sys/mman.h>
22
+ #include <sys/time.h>
23
+ #include <sys/resource.h>
24
+
25
+ /* *****************************************************************************
26
+ Use spinlocks "spnlock.h".
27
+
28
+ For portability, it's possible copy "spnlock.h" directly after this line.
29
+ */
30
+ #include "spnlock.h"
31
+
32
+ /* *****************************************************************************
33
+ Support `libreact` on_close callback, if exist.
34
+ */
35
+
36
+ #pragma weak reactor_on_close
37
+ void reactor_on_close(intptr_t uuid) {}
38
+ #pragma weak reactor_remove
39
+ int reactor_remove(intptr_t uuid) { return -1; }
40
+
41
+ /* *****************************************************************************
42
+ Support timeout setting.
43
+ */
44
+ #pragma weak sock_touch
45
+ void sock_touch(intptr_t uuid) {}
46
+
47
+ /* *****************************************************************************
48
+ OS Sendfile settings.
49
+ */
50
+
51
+ #ifndef USE_SENDFILE
52
+
53
+ #if defined(__linux__) /* linux sendfile works */
54
+ #include <sys/sendfile.h>
55
+ #define USE_SENDFILE 1
56
+ #elif defined(__unix__) /* BSD sendfile should work, but isn't tested */
57
+ #include <sys/uio.h>
58
+ #define USE_SENDFILE 0
59
+ #elif defined(__APPLE__) /* AIs the pple sendfile still broken? */
60
+ #include <sys/uio.h>
61
+ #define USE_SENDFILE 1
62
+ #else /* sendfile might not be available - always set to 0 */
63
+ #define USE_SENDFILE 0
64
+ #endif
65
+
66
+ #endif
67
+
68
+ /* *****************************************************************************
69
+ Buffer and socket map memory allocation. Defaults to mmap.
70
+ */
71
+ #ifndef USE_MALLOC
72
+ #define USE_MALLOC 0
73
+ #endif
74
+
75
+ /* *****************************************************************************
76
+ Library related helper functions
77
+ */
78
+
79
+ /**
80
+ Sets a socket to non blocking state.
81
+ */
82
+ inline int sock_set_non_block(int fd) // Thanks to Bjorn Reese
83
+ {
84
+ /* If they have O_NONBLOCK, use the Posix way to do it */
85
+ #if defined(O_NONBLOCK)
86
+ /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
87
+ int flags;
88
+ if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
89
+ flags = 0;
90
+ // printf("flags initial value was %d\n", flags);
91
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
92
+ #else
93
+ /* Otherwise, use the old way of doing it */
94
+ static int flags = 1;
95
+ return ioctl(fd, FIOBIO, &flags);
96
+ #endif
97
+ }
98
+
99
+ /**
100
+ Gets the maximum number of file descriptors this process can be allowed to
101
+ access (== maximum fd value + 1).
102
+ */
103
+ ssize_t sock_max_capacity(void) {
104
+ // get current limits
105
+ static ssize_t flim = 0;
106
+ if (flim)
107
+ return flim;
108
+ #ifdef _SC_OPEN_MAX
109
+ flim = sysconf(_SC_OPEN_MAX);
110
+ #elif defined(OPEN_MAX)
111
+ flim = OPEN_MAX;
112
+ #endif
113
+ // try to maximize limits - collect max and set to max
114
+ struct rlimit rlim;
115
+ getrlimit(RLIMIT_NOFILE, &rlim);
116
+ // printf("Meximum open files are %llu out of %llu\n", rlim.rlim_cur,
117
+ // rlim.rlim_max);
118
+ rlim.rlim_cur = rlim.rlim_max;
119
+ setrlimit(RLIMIT_NOFILE, &rlim);
120
+ getrlimit(RLIMIT_NOFILE, &rlim);
121
+ // printf("Meximum open files are %llu out of %llu\n", rlim.rlim_cur,
122
+ // rlim.rlim_max);
123
+ // if the current limit is higher than it was, update
124
+ if (flim < rlim.rlim_cur)
125
+ flim = rlim.rlim_cur;
126
+ // return what we have
127
+ return flim;
128
+ }
129
+
130
+ /* *****************************************************************************
131
+ Library Core Data
132
+ */
133
+
134
+ typedef struct {
135
+ /** write buffer - a linked list */
136
+ sock_packet_s *packet;
137
+ /** The fd UUID for the current connection */
138
+ fduuid_u fduuid;
139
+ /** the amount of data sent from the current buffer packet */
140
+ uint32_t sent;
141
+ /** state lock */
142
+ spn_lock_i lock;
143
+ /* -- state flags -- */
144
+ /** Connection is open */
145
+ unsigned open : 1;
146
+ /** indicated that the connection should be closed. */
147
+ unsigned close : 1;
148
+ /** indicated that the connection experienced an error. */
149
+ unsigned err : 1;
150
+ /** future flags. */
151
+ unsigned rsv : 5;
152
+ /* -- placement enforces padding to guaranty memory alignment -- */
153
+ /** Read/Write hooks. */
154
+ sock_rw_hook_s *rw_hooks;
155
+ } fd_info_s;
156
+
157
+ #define LIB_SOCK_STATE_OPEN 1
158
+ #define LIB_SOCK_STATE_CLOSED 0
159
+
160
+ static fd_info_s *fd_info = NULL;
161
+ static size_t fd_capacity = 0;
162
+
163
+ #define uuid2info(uuid) fd_info[sock_uuid2fd(uuid)]
164
+ #define is_valid(uuid) \
165
+ (fd_info[sock_uuid2fd(uuid)].fduuid.data.counter == \
166
+ ((fduuid_u)(uuid)).data.counter && \
167
+ uuid2info(uuid).open)
168
+
169
+ static struct {
170
+ sock_packet_s *pool;
171
+ sock_packet_s *allocated;
172
+ spn_lock_i lock;
173
+ } buffer_pool = {.lock = SPN_LOCK_INIT};
174
+
175
+ #define BUFFER_PACKET_REAL_SIZE (sizeof(sock_packet_s) + BUFFER_PACKET_SIZE)
176
+
177
+ /* reset a socket state */
178
+ static inline void set_fd(int fd, unsigned int state) {
179
+ fd_info_s old_data;
180
+ // lock and update
181
+ spn_lock(&fd_info[fd].lock);
182
+ old_data = fd_info[fd];
183
+ fd_info[fd] = (fd_info_s){
184
+ .fduuid.data.counter = fd_info[fd].fduuid.data.counter + state,
185
+ .fduuid.data.fd = fd,
186
+ .lock = fd_info[fd].lock,
187
+ .open = state,
188
+ };
189
+ // unlock
190
+ spn_unlock(&fd_info[fd].lock);
191
+ // should be called within the lock? - no function calling within a spinlock.
192
+ if (old_data.rw_hooks && old_data.rw_hooks->on_clear)
193
+ old_data.rw_hooks->on_clear(old_data.fduuid.uuid, old_data.rw_hooks);
194
+ // clear old data
195
+ if (old_data.packet)
196
+ sock_free_packet(old_data.packet);
197
+ // call callback if exists
198
+ if (old_data.open) {
199
+ // if (state == LIB_SOCK_STATE_OPEN)
200
+ // printf(
201
+ // "STRONG FD COLLISION PROTECTION: A new connection was accepted "
202
+ // "while the old one was marked as open.\n");
203
+ reactor_remove(old_data.fduuid.uuid);
204
+ reactor_on_close(old_data.fduuid.uuid);
205
+ }
206
+ }
207
+
208
+ /**
209
+ Destroys the library data.
210
+
211
+ Call this function before calling any `libsock` functions.
212
+ */
213
+ static void destroy_lib_data(void) {
214
+ if (fd_info) {
215
+ while (fd_capacity--) { // include 0 in countdown
216
+ set_fd(fd_capacity, LIB_SOCK_STATE_CLOSED);
217
+ }
218
+ #if USE_MALLOC == 1
219
+ free(fd_info);
220
+ #else
221
+ munmap(fd_info, (BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL) +
222
+ (sizeof(fd_info_s) * fd_capacity));
223
+ #endif
224
+ }
225
+ fd_info = NULL;
226
+ buffer_pool.pool = NULL;
227
+ buffer_pool.allocated = NULL;
228
+ buffer_pool.lock = SPN_LOCK_INIT;
229
+ }
230
+
231
+ /**
232
+ Initializes the library.
233
+
234
+ Call this function before calling any `libsock` functions.
235
+ */
236
+ static void sock_lib_init(void) {
237
+ if (fd_info)
238
+ return;
239
+
240
+ fd_capacity = sock_max_capacity();
241
+ size_t fd_map_mem_size = sizeof(fd_info_s) * fd_capacity;
242
+ size_t buffer_mem_size = BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL;
243
+
244
+ void *buff_mem;
245
+ #if USE_MALLOC == 1
246
+ buff_mem = malloc(fd_map_mem_size + buffer_mem_size);
247
+ if (buff_mem == NULL) {
248
+ perror("Couldn't initialize libsock - not enough memory? ");
249
+ exit(1);
250
+ }
251
+ #else
252
+ buff_mem = mmap(NULL, fd_map_mem_size + buffer_mem_size,
253
+ PROT_READ | PROT_WRITE | PROT_EXEC,
254
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
255
+ // MAP_SHARED | MAP_ANONYMOUS, -1, 0);
256
+ if (buff_mem == MAP_FAILED || buff_mem == NULL) {
257
+ perror("Couldn't initialize libsock - not enough memory? ");
258
+ exit(1);
259
+ }
260
+ #endif
261
+ fd_info = buff_mem;
262
+ for (size_t i = 0; i < fd_capacity; i++) {
263
+ fd_info[i] = (fd_info_s){.lock = SPN_LOCK_INIT};
264
+ spn_unlock(&fd_info[i].lock);
265
+ }
266
+ /* initialize pool */
267
+ buffer_pool.allocated = buff_mem + fd_map_mem_size;
268
+ buffer_pool.pool = buffer_pool.allocated;
269
+ sock_packet_s *pos = buffer_pool.pool;
270
+ for (size_t i = 0; i < BUFFER_PACKET_POOL - 1; i++) {
271
+ *pos = (sock_packet_s){
272
+ .metadata.next = (void *)(((uintptr_t)pos) + BUFFER_PACKET_REAL_SIZE),
273
+ };
274
+ pos = pos->metadata.next;
275
+ }
276
+ pos->metadata.next = 0;
277
+ /* deallocate and manage on exit */
278
+ atexit(destroy_lib_data);
279
+ #ifdef DEBUG
280
+ fprintf(stderr, "\nInitialized libsock for %lu sockets, "
281
+ "each one requires %lu bytes.\n"
282
+ "overall ovearhead: %lu bytes.\n"
283
+ "Initialized packet pool for %d elements, "
284
+ "each one %lu bytes.\n"
285
+ "overall buffer ovearhead: %lu bytes.\n"
286
+ "=== Total: %lu bytes ===\n\n",
287
+ fd_capacity, sizeof(*fd_info), sizeof(*fd_info) * fd_capacity,
288
+ BUFFER_PACKET_POOL, BUFFER_PACKET_REAL_SIZE,
289
+ BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL,
290
+ (BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL) +
291
+ (sizeof(*fd_info) * fd_capacity));
292
+ #endif
293
+ }
294
+
295
+ #define review_lib() \
296
+ if (fd_info == NULL) \
297
+ sock_lib_init();
298
+
299
+ /* *****************************************************************************
300
+ Read / Write internals
301
+ */
302
+
303
+ #define ERR_OK (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN)
304
+ #define ERR_TRY_AGAIN (errno == EINTR)
305
+
306
+ static inline int sock_flush_fd_failed(int fd) {
307
+ sock_free_packet(fd_info[fd].packet);
308
+ fd_info[fd].packet = NULL;
309
+ fd_info[fd].close = 1;
310
+ fd_info[fd].err = 1;
311
+ return 0;
312
+ }
313
+
314
+ #if USE_SENDFILE == 1
315
+
316
+ #if defined(__linux__) /* linux sendfile API */
317
+ static inline int sock_flush_os_sendfile(int fd) {
318
+ size_t sent;
319
+ sock_packet_s *packet = fd_info[fd].packet;
320
+ sent =
321
+ sendfile64(fd, (int)((ssize_t)packet->buffer), &packet->metadata.offset,
322
+ packet->length - fd_info[fd].sent);
323
+
324
+ if (sent < 0) {
325
+ if (ERR_OK)
326
+ return -1;
327
+ else if (ERR_TRY_AGAIN)
328
+ return 0;
329
+ else
330
+ return sock_flush_fd_failed(fd);
331
+ }
332
+ if (sent == 0)
333
+ fd_info[fd].sent = packet->length;
334
+ fd_info[fd].sent += sent;
335
+ return 0;
336
+ }
337
+
338
+ #elif defined(__APPLE__) || defined(__unix__) /* BSD / Apple API */
339
+
340
+ static inline int sock_flush_os_sendfile(int fd) {
341
+ off_t act_sent;
342
+ sock_packet_s *packet = fd_info[fd].packet;
343
+ act_sent = packet->length - fd_info[fd].sent;
344
+
345
+ #if defined(__APPLE__)
346
+ if (sendfile((int)((ssize_t)packet->buffer), fd, packet->metadata.offset,
347
+ &act_sent, NULL, 0) < 0 &&
348
+ act_sent == 0)
349
+ #else
350
+ if (sendfile((int)((ssize_t)packet->buffer), fd, packet->metadata.offset,
351
+ (size_t)act_sent, NULL, &act_sent, 0) < 0 &&
352
+ act_sent == 0)
353
+ #endif
354
+ {
355
+ if (ERR_OK)
356
+ return -1;
357
+ else if (ERR_TRY_AGAIN)
358
+ return 0;
359
+ else
360
+ return sock_flush_fd_failed(fd);
361
+ }
362
+ if (act_sent == 0) {
363
+ fd_info[fd].sent = packet->length;
364
+ return 0;
365
+ }
366
+ packet->metadata.offset += act_sent;
367
+ fd_info[fd].sent += act_sent;
368
+ return 0;
369
+ }
370
+ #endif
371
+
372
+ #else
373
+
374
+ static inline int sock_flush_os_sendfile(int fd) { return -1; }
375
+
376
+ #endif
377
+
378
+ static inline int sock_flush_fd(int fd) {
379
+ if (USE_SENDFILE && fd_info[fd].rw_hooks == NULL)
380
+ return sock_flush_os_sendfile(fd);
381
+ ssize_t sent;
382
+ sock_packet_s *packet = fd_info[fd].packet;
383
+ // how much data are we expecting to send...?
384
+ ssize_t i_exp = (BUFFER_PACKET_SIZE > packet->length) ? packet->length
385
+ : BUFFER_PACKET_SIZE;
386
+
387
+ // read data into the internal buffer
388
+ if (packet->metadata.internal_flag == 0) {
389
+ ssize_t i_read;
390
+ i_read = pread((int)((ssize_t)packet->buffer), packet + 1, i_exp,
391
+ packet->metadata.offset);
392
+ if (i_read <= 0) {
393
+ fd_info[fd].sent = fd_info[fd].packet->length;
394
+ return 0;
395
+ } else {
396
+ packet->metadata.offset += i_read;
397
+ packet->metadata.internal_flag = 1;
398
+ }
399
+ }
400
+ // send the data
401
+ if (fd_info[fd].rw_hooks && fd_info[fd].rw_hooks->write)
402
+ sent = fd_info[fd].rw_hooks->write(
403
+ fd_info[fd].fduuid.uuid, (((void *)(packet + 1)) + fd_info[fd].sent),
404
+ i_exp - fd_info[fd].sent);
405
+ else
406
+ sent = write(fd, (((void *)(packet + 1)) + fd_info[fd].sent),
407
+ i_exp - fd_info[fd].sent);
408
+ // review result and update packet data
409
+ if (sent < 0) {
410
+ if (ERR_OK)
411
+ return -1;
412
+ else if (ERR_TRY_AGAIN)
413
+ return 0;
414
+ else
415
+ return sock_flush_fd_failed(fd);
416
+ }
417
+ fd_info[fd].sent += sent;
418
+ if (fd_info[fd].sent >= i_exp) {
419
+ packet->metadata.internal_flag = 0;
420
+ fd_info[fd].sent = 0;
421
+ packet->length -= i_exp;
422
+ }
423
+ return 0;
424
+ }
425
+
426
+ static inline int sock_flush_data(int fd) {
427
+ ssize_t sent;
428
+ if (fd_info[fd].rw_hooks && fd_info[fd].rw_hooks->write)
429
+ sent = fd_info[fd].rw_hooks->write(
430
+ fd_info[fd].fduuid.uuid, fd_info[fd].packet->buffer + fd_info[fd].sent,
431
+ fd_info[fd].packet->length - fd_info[fd].sent);
432
+ else
433
+ sent = write(fd, fd_info[fd].packet->buffer + fd_info[fd].sent,
434
+ fd_info[fd].packet->length - fd_info[fd].sent);
435
+ if (sent < 0) {
436
+ if (ERR_OK)
437
+ return -1;
438
+ else if (ERR_TRY_AGAIN)
439
+ return 0;
440
+ else
441
+ return sock_flush_fd_failed(fd);
442
+ }
443
+ fd_info[fd].sent += sent;
444
+ return 0;
445
+ }
446
+
447
+ static void sock_flush_unsafe(int fd) {
448
+ while (fd_info[fd].packet) {
449
+ if (fd_info[fd].packet->metadata.is_fd == 0) {
450
+ if (sock_flush_data(fd))
451
+ return;
452
+ } else {
453
+ if (sock_flush_fd(fd))
454
+ return;
455
+ }
456
+ if (fd_info[fd].packet && fd_info[fd].packet->length <= fd_info[fd].sent) {
457
+ sock_packet_s *packet = fd_info[fd].packet;
458
+ fd_info[fd].packet = packet->metadata.next;
459
+ packet->metadata.next = NULL;
460
+ fd_info[fd].sent = 0;
461
+ sock_free_packet(packet);
462
+ }
463
+ }
464
+ }
465
+
466
+ static inline void sock_send_packet_unsafe(int fd, sock_packet_s *packet) {
467
+ fd_info_s *sfd = fd_info + fd;
468
+ if (sfd->packet == NULL) {
469
+ /* no queue, nothing to check */
470
+ sfd->packet = packet;
471
+ sock_flush_unsafe(fd);
472
+ return;
473
+
474
+ } else if (packet->metadata.urgent == 0) {
475
+ /* not urgent, last in line */
476
+ sock_packet_s *pos = sfd->packet;
477
+ while (pos->metadata.next)
478
+ pos = pos->metadata.next;
479
+ pos->metadata.next = packet;
480
+ sock_flush_unsafe(fd);
481
+ return;
482
+
483
+ } else {
484
+ /* urgent, find a spot we can interrupt */
485
+ sock_packet_s **pos = &sfd->packet;
486
+ while (*pos && (*pos)->metadata.can_interrupt == 0)
487
+ pos = &(*pos)->metadata.next;
488
+ sock_packet_s *tail = *pos;
489
+ *pos = packet;
490
+ if (tail) {
491
+ pos = &packet->metadata.next;
492
+ while (*pos)
493
+ pos = &(*pos)->metadata.next;
494
+ *pos = tail;
495
+ }
496
+ }
497
+ sock_flush_unsafe(fd);
498
+ }
499
+
500
+ /* *****************************************************************************
501
+ Listen
502
+ */
503
+
504
+ /**
505
+ Opens a listening non-blocking socket. Return's the socket's UUID.
506
+ */
507
+ intptr_t sock_listen(const char *address, const char *port) {
508
+ review_lib();
509
+ int srvfd;
510
+ // setup the address
511
+ struct addrinfo hints;
512
+ struct addrinfo *servinfo; // will point to the results
513
+ memset(&hints, 0, sizeof hints); // make sure the struct is empty
514
+ hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
515
+ hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
516
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
517
+ if (getaddrinfo(address, port, &hints, &servinfo)) {
518
+ // perror("addr err");
519
+ return -1;
520
+ }
521
+ // get the file descriptor
522
+ srvfd =
523
+ socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
524
+ if (srvfd <= 0) {
525
+ // perror("socket err");
526
+ freeaddrinfo(servinfo);
527
+ return -1;
528
+ }
529
+ // make sure the socket is non-blocking
530
+ if (sock_set_non_block(srvfd) < 0) {
531
+ // perror("couldn't set socket as non blocking! ");
532
+ freeaddrinfo(servinfo);
533
+ close(srvfd);
534
+ return -1;
535
+ }
536
+ // avoid the "address taken"
537
+ {
538
+ int optval = 1;
539
+ setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
540
+ }
541
+ // bind the address to the socket
542
+ {
543
+ int bound = 0;
544
+ for (struct addrinfo *p = servinfo; p != NULL; p = p->ai_next) {
545
+ if (!bind(srvfd, p->ai_addr, p->ai_addrlen))
546
+ bound = 1;
547
+ }
548
+
549
+ if (!bound) {
550
+ // perror("bind err");
551
+ freeaddrinfo(servinfo);
552
+ close(srvfd);
553
+ return -1;
554
+ }
555
+ }
556
+ freeaddrinfo(servinfo);
557
+ // listen in
558
+ if (listen(srvfd, SOMAXCONN) < 0) {
559
+ // perror("couldn't start listening");
560
+ close(srvfd);
561
+ return -1;
562
+ }
563
+ set_fd(srvfd, LIB_SOCK_STATE_OPEN);
564
+ return fd_info[srvfd].fduuid.uuid;
565
+ }
566
+
567
+ /* *****************************************************************************
568
+ Accept
569
+ */
570
+
571
+ intptr_t sock_accept(intptr_t srv_uuid) {
572
+ review_lib();
573
+ static socklen_t cl_addrlen = 0;
574
+ int client;
575
+ #ifdef SOCK_NONBLOCK
576
+ client = accept4(sock_uuid2fd(srv_uuid), NULL, &cl_addrlen, SOCK_NONBLOCK);
577
+ if (client <= 0)
578
+ return -1;
579
+ #else
580
+ client = accept(sock_uuid2fd(srv_uuid), NULL, &cl_addrlen);
581
+ if (client <= 0)
582
+ return -1;
583
+ sock_set_non_block(client);
584
+ #endif
585
+ set_fd(client, LIB_SOCK_STATE_OPEN);
586
+ return fd_info[client].fduuid.uuid;
587
+ }
588
+
589
+ /* *****************************************************************************
590
+ Connect
591
+ */
592
+ intptr_t sock_connect(char *address, char *port) {
593
+ review_lib();
594
+ int fd;
595
+ // setup the address
596
+ struct addrinfo hints;
597
+ struct addrinfo *addrinfo; // will point to the results
598
+ memset(&hints, 0, sizeof hints); // make sure the struct is empty
599
+ hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
600
+ hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
601
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
602
+ if (getaddrinfo(address, port, &hints, &addrinfo)) {
603
+ return -1;
604
+ }
605
+ // get the file descriptor
606
+ fd =
607
+ socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
608
+ if (fd <= 0) {
609
+ freeaddrinfo(addrinfo);
610
+ return -1;
611
+ }
612
+ // make sure the socket is non-blocking
613
+ if (sock_set_non_block(fd) < 0) {
614
+ freeaddrinfo(addrinfo);
615
+ close(fd);
616
+ return -1;
617
+ }
618
+
619
+ if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) < 0 &&
620
+ errno != EINPROGRESS) {
621
+ close(fd);
622
+ freeaddrinfo(addrinfo);
623
+ return -1;
624
+ }
625
+ freeaddrinfo(addrinfo);
626
+ set_fd(fd, LIB_SOCK_STATE_OPEN);
627
+ return fd_info[fd].fduuid.uuid;
628
+ }
629
+
630
+ /* *****************************************************************************
631
+ Open existing
632
+ */
633
+
634
+ intptr_t sock_open(int fd) {
635
+ review_lib();
636
+ set_fd(fd, LIB_SOCK_STATE_OPEN);
637
+ return fd_info[fd].fduuid.uuid;
638
+ }
639
+
640
+ /* *****************************************************************************
641
+ Information about the socket
642
+ */
643
+
644
+ /**
645
+ Returns 1 if the uuid refers to a valid and open, socket.
646
+
647
+ Returns 0 if not.
648
+ */
649
+ int sock_isvalid(intptr_t uuid) { return fd_info && is_valid(uuid); }
650
+
651
+ /**
652
+ `sock_fd2uuid` takes an existing file decriptor `fd` and returns it's active
653
+ `uuid`.
654
+ */
655
+ intptr_t sock_fd2uuid(int fd) {
656
+ return (fd_info && fd_info[fd].open) ? fd_info[fd].fduuid.uuid : -1;
657
+ }
658
+
659
+ /* *****************************************************************************
660
+ Buffer API.
661
+ */
662
+
663
+ static inline sock_packet_s *sock_try_checkout_packet(void) {
664
+ sock_packet_s *packet;
665
+ spn_lock(&buffer_pool.lock);
666
+ packet = buffer_pool.pool;
667
+ if (packet) {
668
+ buffer_pool.pool = packet->metadata.next;
669
+ spn_unlock(&buffer_pool.lock);
670
+ *packet = (sock_packet_s){.buffer = packet + 1, .metadata.next = NULL};
671
+ return packet;
672
+ }
673
+ spn_unlock(&buffer_pool.lock);
674
+ return packet;
675
+ }
676
+
677
+ /**
678
+ Checks out a `sock_packet_s` from the packet pool, transfering the
679
+ ownership of the memory to the calling function. The function will hang until a
680
+ packet becomes available, so never check out more then a single packet at a
681
+ time.
682
+ */
683
+ sock_packet_s *sock_checkout_packet(void) {
684
+ review_lib();
685
+ sock_packet_s *packet = NULL;
686
+ for (;;) {
687
+ spn_lock(&buffer_pool.lock);
688
+ packet = buffer_pool.pool;
689
+ if (packet) {
690
+ buffer_pool.pool = packet->metadata.next;
691
+ spn_unlock(&buffer_pool.lock);
692
+ *packet = (sock_packet_s){.buffer = packet + 1, .metadata.next = NULL};
693
+ return packet;
694
+ }
695
+ spn_unlock(&buffer_pool.lock);
696
+ reschedule_thread();
697
+ sock_flush_all();
698
+ }
699
+ }
700
+ /**
701
+ Attaches a packet to a socket's output buffer and calls `sock_flush` for the
702
+ socket.
703
+ */
704
+ ssize_t sock_send_packet(intptr_t uuid, sock_packet_s *packet) {
705
+ if (!fd_info || !is_valid(uuid)) {
706
+ sock_free_packet(packet);
707
+ return -1;
708
+ }
709
+ spn_lock(&uuid2info(uuid).lock);
710
+ sock_send_packet_unsafe(sock_uuid2fd(uuid), packet);
711
+ spn_unlock(&uuid2info(uuid).lock);
712
+ return 0;
713
+ }
714
+
715
+ /**
716
+ Use `sock_free_packet` to free unused packets that were checked-out using
717
+ `sock_checkout_packet`.
718
+ */
719
+ void sock_free_packet(sock_packet_s *packet) {
720
+ sock_packet_s *next = packet;
721
+ if (packet == NULL)
722
+ return;
723
+ for (;;) {
724
+ if (next->metadata.is_fd) {
725
+ if (next->metadata.keep_open == 0)
726
+ close((int)((ssize_t)next->buffer));
727
+ } else if (next->metadata.external)
728
+ free(next->buffer);
729
+ if (next->metadata.next == NULL)
730
+ break; /* next will hold the last packet in the chain. */
731
+ next = next->metadata.next;
732
+ }
733
+ spn_lock(&buffer_pool.lock);
734
+ next->metadata.next = buffer_pool.pool;
735
+ buffer_pool.pool = packet;
736
+ spn_unlock(&buffer_pool.lock);
737
+ }
738
+
739
+ /* *****************************************************************************
740
+ Reading
741
+ */
742
+ ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
743
+ if (!fd_info || !is_valid(uuid)) {
744
+ errno = ENODEV;
745
+ return -1;
746
+ }
747
+ ssize_t i_read;
748
+ fd_info_s *sfd = fd_info + sock_uuid2fd(uuid);
749
+ if (sfd->rw_hooks && sfd->rw_hooks->read)
750
+ i_read = sfd->rw_hooks->read(uuid, buf, count);
751
+ else
752
+ i_read = read(sock_uuid2fd(uuid), buf, count);
753
+
754
+ if (i_read > 0) {
755
+ sock_touch(uuid);
756
+ return i_read;
757
+ }
758
+ if (i_read == -1 && (ERR_OK || ERR_TRY_AGAIN))
759
+ return 0;
760
+ // fprintf(stderr, "Read Error for %lu bytes from fd %d (closing))\n", count,
761
+ // sock_uuid2fd(uuid));
762
+ sock_close(uuid);
763
+ return -1;
764
+ }
765
+
766
+ /* *****************************************************************************
767
+ Flushing
768
+ */
769
+
770
+ ssize_t sock_flush(intptr_t uuid) {
771
+ if (!fd_info || !is_valid(uuid))
772
+ return -1;
773
+ spn_lock(&uuid2info(uuid).lock);
774
+ sock_flush_unsafe(sock_uuid2fd(uuid));
775
+ spn_unlock(&uuid2info(uuid).lock);
776
+ if (uuid2info(uuid).close) {
777
+ sock_force_close(uuid);
778
+ return -1;
779
+ }
780
+ return 0;
781
+ }
782
+ /**
783
+ `sock_flush_strong` performs the same action as `sock_flush` but returns only
784
+ after all the data was sent. This is an "active" wait, polling isn't
785
+ performed.
786
+ */
787
+ void sock_flush_strong(intptr_t uuid) {
788
+ if (!fd_info)
789
+ return;
790
+ while (is_valid(uuid) && uuid2info(uuid).packet)
791
+ sock_flush(uuid);
792
+ }
793
+ /**
794
+ Calls `sock_flush` for each file descriptor that's buffer isn't empty.
795
+ */
796
+ void sock_flush_all(void) {
797
+ for (size_t i = 0; i < fd_capacity; i++) {
798
+ if (fd_info[i].packet == NULL || spn_is_locked(&fd_info[i].lock))
799
+ continue;
800
+ sock_flush(fd_info[i].fduuid.uuid);
801
+ }
802
+ }
803
+
804
+ /* *****************************************************************************
805
+ Writing
806
+ */
807
+
808
+ ssize_t sock_write2_fn(sock_write_info_s options) {
809
+ if (!fd_info || !is_valid(options.fduuid)) {
810
+ errno = ENODEV;
811
+ return -1;
812
+ }
813
+ if (options.buffer == NULL)
814
+ return -1;
815
+ if (!options.length && !options.is_fd)
816
+ options.length = strlen(options.buffer);
817
+ if (options.length == 0)
818
+ return -1;
819
+ sock_packet_s *packet = sock_checkout_packet();
820
+ packet->metadata.can_interrupt = 1;
821
+ packet->metadata.urgent = options.urgent;
822
+
823
+ if (options.is_fd) {
824
+ packet->buffer = (void *)options.buffer;
825
+ packet->length = options.length;
826
+ packet->metadata.is_fd = options.is_fd;
827
+ packet->metadata.offset = options.offset;
828
+ return sock_send_packet(options.fduuid, packet);
829
+ } else {
830
+ if (options.move) {
831
+ packet->buffer = (void *)options.buffer;
832
+ packet->length = options.length;
833
+ packet->metadata.external = 1;
834
+ return sock_send_packet(options.fduuid, packet);
835
+ } else {
836
+ if (options.length <= BUFFER_PACKET_SIZE) {
837
+ memcpy(packet->buffer, options.buffer, options.length);
838
+ packet->length = options.length;
839
+ return sock_send_packet(options.fduuid, packet);
840
+ } else {
841
+ if (packet->metadata.urgent) {
842
+ fprintf(stderr, "Socket err:"
843
+ "Large data cannot be sent as an urgent packet.\n"
844
+ "Urgency silently ignored\n");
845
+ packet->metadata.urgent = 0;
846
+ }
847
+ size_t to_cpy;
848
+ spn_lock(&uuid2info(options.fduuid).lock);
849
+ for (;;) {
850
+ to_cpy = options.length > BUFFER_PACKET_SIZE ? BUFFER_PACKET_SIZE
851
+ : options.length;
852
+ memcpy(packet->buffer, options.buffer, to_cpy);
853
+ packet->length = to_cpy;
854
+ options.length -= to_cpy;
855
+ options.buffer += to_cpy;
856
+ sock_send_packet_unsafe(sock_uuid2fd(options.fduuid), packet);
857
+ if (!is_valid(options.fduuid) || uuid2info(options.fduuid).err == 1 ||
858
+ options.length == 0)
859
+ break;
860
+ packet = sock_try_checkout_packet();
861
+ while (packet == NULL) {
862
+ sock_flush_all();
863
+ sock_flush_unsafe(sock_uuid2fd(options.fduuid));
864
+ packet = sock_try_checkout_packet();
865
+ }
866
+ }
867
+ spn_unlock(&uuid2info(options.fduuid).lock);
868
+ if (uuid2info(options.fduuid).packet == NULL &&
869
+ uuid2info(options.fduuid).close) {
870
+ sock_force_close(options.fduuid);
871
+ return -1;
872
+ }
873
+ return is_valid(options.fduuid) ? 0 : -1;
874
+ }
875
+ }
876
+ }
877
+ // how did we get here?
878
+ return -1;
879
+ }
880
+
881
+ /* *****************************************************************************
882
+ Closing.
883
+ */
884
+
885
+ void sock_close(intptr_t uuid) {
886
+ // fprintf(stderr, "called sock_close for %lu (%d)\n", uuid,
887
+ // sock_uuid2fd(uuid));
888
+ if (!fd_info || !is_valid(uuid))
889
+ return;
890
+ fd_info[sock_uuid2fd(uuid)].close = 1;
891
+ sock_flush(uuid);
892
+ }
893
+
894
+ void sock_force_close(intptr_t uuid) {
895
+ // fprintf(stderr, "called sock_force_close for %lu (%d)\n", uuid,
896
+ // sock_uuid2fd(uuid));
897
+ if (!fd_info || !is_valid(uuid))
898
+ return;
899
+ shutdown(sock_uuid2fd(uuid), SHUT_RDWR);
900
+ close(sock_uuid2fd(uuid));
901
+ set_fd(sock_uuid2fd(uuid), LIB_SOCK_STATE_CLOSED);
902
+ }
903
+
904
+ /* *****************************************************************************
905
+ RW hooks implementation
906
+ */
907
+
908
+ /** Gets a socket hook state (a pointer to the struct). */
909
+ struct sock_rw_hook_s *sock_rw_hook_get(intptr_t uuid) {
910
+ if (!fd_info || !is_valid(uuid))
911
+ return NULL;
912
+ return uuid2info(uuid).rw_hooks;
913
+ }
914
+
915
+ /** Sets a socket hook state (a pointer to the struct). */
916
+ int sock_rw_hook_set(intptr_t uuid, sock_rw_hook_s *rw_hooks) {
917
+ if (!fd_info || !is_valid(uuid))
918
+ return -1;
919
+ spn_lock(&(uuid2info(uuid).lock));
920
+ uuid2info(uuid).rw_hooks = rw_hooks;
921
+ spn_unlock(&uuid2info(uuid).lock);
922
+ return 0;
923
+ }
924
+
925
+ /* *****************************************************************************
926
+ test
927
+ */
928
+ #ifdef DEBUG
929
+ void sock_libtest(void) {
930
+ sock_lib_init();
931
+ sock_packet_s *p, *pl;
932
+ size_t count = 0;
933
+ fprintf(stderr, "Testing packet pool\n");
934
+ for (size_t i = 0; i < BUFFER_PACKET_POOL * 2; i++) {
935
+ count = 1;
936
+ pl = p = sock_checkout_packet();
937
+ while (buffer_pool.pool) {
938
+ count++;
939
+ pl->metadata.next = sock_checkout_packet();
940
+ pl = pl->metadata.next;
941
+ }
942
+ sock_free_packet(p);
943
+ // fprintf(stderr, "Collected and freed %lu packets.\n", count);
944
+ }
945
+ fprintf(stderr,
946
+ "liniar (no-contention) packet checkout + free shows %lu packets. "
947
+ "test %s\n",
948
+ count, count == BUFFER_PACKET_POOL ? "passed." : "FAILED!");
949
+ }
950
+ #endif