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,1025 +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
-
11
- #include "libsock.h"
12
-
13
- #include <errno.h>
14
- #include <fcntl.h>
15
- #include <limits.h>
16
- #include <netdb.h>
17
- #include <stdio.h>
18
- #include <string.h>
19
- #include <sys/mman.h>
20
- #include <sys/resource.h>
21
- #include <sys/socket.h>
22
- #include <sys/time.h>
23
- #include <sys/types.h>
24
- #include <time.h>
25
-
26
- /* *****************************************************************************
27
- Use spinlocks "spnlock.h".
28
-
29
- For portability, it's possible copy "spnlock.h" directly after this line.
30
- */
31
- #include "spnlock.h"
32
-
33
- /* *****************************************************************************
34
- Support `libreact` on_close callback, if exist.
35
- */
36
-
37
- #pragma weak reactor_on_close
38
- void reactor_on_close(intptr_t uuid) { (void)(uuid); }
39
- #pragma weak reactor_remove
40
- int reactor_remove(intptr_t uuid) {
41
- (void)(uuid);
42
- return -1;
43
- }
44
-
45
- /* *****************************************************************************
46
- Support timeout setting.
47
- */
48
- #pragma weak sock_touch
49
- void sock_touch(intptr_t uuid) { (void)(uuid); }
50
-
51
- /* *****************************************************************************
52
- Support event based `write` scheduling.
53
- */
54
- #pragma weak async_run
55
- int async_run(void (*task)(void *), void *arg) {
56
- (void)(task);
57
- (void)(arg);
58
- return -1;
59
- }
60
-
61
- /* *****************************************************************************
62
- OS Sendfile settings.
63
- */
64
-
65
- #ifndef USE_SENDFILE
66
-
67
- #if defined(__linux__) /* linux sendfile works */
68
- #include <sys/sendfile.h>
69
- #define USE_SENDFILE 1
70
- #elif defined(__unix__) /* BSD sendfile should work, but isn't tested */
71
- #include <sys/uio.h>
72
- #define USE_SENDFILE 0
73
- #elif defined(__APPLE__) /* Is the apple sendfile still broken? */
74
- #include <sys/uio.h>
75
- #define USE_SENDFILE 1
76
- #else /* sendfile might not be available - always set to 0 */
77
- #define USE_SENDFILE 0
78
- #endif
79
-
80
- #endif
81
-
82
- /* *****************************************************************************
83
- Buffer and socket map memory allocation. Defaults to mmap.
84
- */
85
- #ifndef USE_MALLOC
86
- #define USE_MALLOC 0
87
- #endif
88
-
89
- /* *****************************************************************************
90
- The system call to `write` (non-blocking) can be defered when using `libasync`.
91
-
92
- However, this will not prevent `sock_write` from cycling through the sockets and
93
- flushing them (block emulation) when both the system and the user level buffers
94
- are full.
95
-
96
- Defaults to 1 (defered).
97
- */
98
- #ifndef SOCK_DELAY_WRITE
99
- #define SOCK_DELAY_WRITE 1
100
- #endif
101
-
102
- /* *****************************************************************************
103
- Library related helper functions
104
- */
105
-
106
- /**
107
- Sets a socket to non blocking state.
108
- */
109
- inline int sock_set_non_block(int fd) // Thanks to Bjorn Reese
110
- {
111
- /* If they have O_NONBLOCK, use the Posix way to do it */
112
- #if defined(O_NONBLOCK)
113
- /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
114
- int flags;
115
- if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
116
- flags = 0;
117
- // printf("flags initial value was %d\n", flags);
118
- return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
119
- #else
120
- /* Otherwise, use the old way of doing it */
121
- static int flags = 1;
122
- return ioctl(fd, FIOBIO, &flags);
123
- #endif
124
- }
125
-
126
- /**
127
- Gets the maximum number of file descriptors this process can be allowed to
128
- access (== maximum fd value + 1).
129
- */
130
- ssize_t sock_max_capacity(void) {
131
- // get current limits
132
- static ssize_t flim = 0;
133
- if (flim)
134
- return flim;
135
- #ifdef _SC_OPEN_MAX
136
- flim = sysconf(_SC_OPEN_MAX);
137
- #elif defined(OPEN_MAX)
138
- flim = OPEN_MAX;
139
- #endif
140
- // try to maximize limits - collect max and set to max
141
- struct rlimit rlim = {.rlim_max = 0};
142
- getrlimit(RLIMIT_NOFILE, &rlim);
143
- // printf("Meximum open files are %llu out of %llu\n", rlim.rlim_cur,
144
- // rlim.rlim_max);
145
- #if defined(__APPLE__) /* Apple's getrlimit is broken. */
146
- rlim.rlim_cur = rlim.rlim_max >= OPEN_MAX ? OPEN_MAX : rlim.rlim_max;
147
- #else
148
- rlim.rlim_cur = rlim.rlim_max;
149
- #endif
150
-
151
- setrlimit(RLIMIT_NOFILE, &rlim);
152
- getrlimit(RLIMIT_NOFILE, &rlim);
153
- // printf("Meximum open files are %llu out of %llu\n", rlim.rlim_cur,
154
- // rlim.rlim_max);
155
- // if the current limit is higher than it was, update
156
- if (flim < ((ssize_t)rlim.rlim_cur))
157
- flim = rlim.rlim_cur;
158
- // return what we have
159
- return flim;
160
- }
161
-
162
- /* *****************************************************************************
163
- Library Core Data
164
- */
165
-
166
- typedef struct {
167
- /** write buffer - a linked list */
168
- sock_packet_s *packet;
169
- /** The fd UUID for the current connection */
170
- fduuid_u fduuid;
171
- /** the amount of data sent from the current buffer packet */
172
- uint32_t sent;
173
- /** state lock */
174
- spn_lock_i lock;
175
- /* -- state flags -- */
176
- /** Connection is open */
177
- unsigned open : 1;
178
- /** indicated that the connection should be closed. */
179
- unsigned close : 1;
180
- /** indicated that the connection experienced an error. */
181
- unsigned err : 1;
182
- /** future flags. */
183
- unsigned rsv : 5;
184
- /* -- placement enforces padding to guaranty memory alignment -- */
185
- /** Read/Write hooks. */
186
- sock_rw_hook_s *rw_hooks;
187
- } fd_info_s;
188
-
189
- #define LIB_SOCK_STATE_OPEN 1
190
- #define LIB_SOCK_STATE_CLOSED 0
191
-
192
- static fd_info_s *fd_info = NULL;
193
- static size_t fd_capacity = 0;
194
-
195
- #define uuid2info(uuid) fd_info[sock_uuid2fd(uuid)]
196
- #define is_valid(uuid) \
197
- (sock_uuid2fd(uuid) >= 0 && sock_uuid2fd(uuid) <= (int)fd_capacity && \
198
- fd_info[sock_uuid2fd(uuid)].fduuid.data.counter == \
199
- ((fduuid_u *)(&uuid))->data.counter && \
200
- uuid2info(uuid).open)
201
-
202
- static struct {
203
- sock_packet_s *volatile pool;
204
- sock_packet_s *allocated;
205
- spn_lock_i lock;
206
- } buffer_pool = {.lock = SPN_LOCK_INIT};
207
-
208
- #define BUFFER_PACKET_REAL_SIZE (sizeof(sock_packet_s) + BUFFER_PACKET_SIZE)
209
-
210
- /* reset a socket state */
211
- static inline void set_fd(int fd, unsigned int state) {
212
- fd_info_s old_data;
213
- // lock and update
214
- spn_lock(&fd_info[fd].lock);
215
- old_data = fd_info[fd];
216
- fd_info[fd] = (fd_info_s){
217
- .fduuid.data.counter = fd_info[fd].fduuid.data.counter + state,
218
- .fduuid.data.fd = fd,
219
- .lock = fd_info[fd].lock,
220
- .open = state,
221
- };
222
- // unlock
223
- spn_unlock(&fd_info[fd].lock);
224
- // should be called within the lock? - no function calling within a
225
- // spinlock.
226
- if (old_data.rw_hooks && old_data.rw_hooks->on_clear)
227
- old_data.rw_hooks->on_clear(old_data.fduuid.uuid, old_data.rw_hooks);
228
- // clear old data
229
- if (old_data.packet)
230
- sock_free_packet(old_data.packet);
231
- // call callback if exists
232
- if (old_data.open) {
233
- // if (state == LIB_SOCK_STATE_OPEN)
234
- // printf(
235
- // "STRONG FD COLLISION PROTECTION: A new connection was accepted "
236
- // "while the old one was marked as open.\n");
237
- reactor_remove(old_data.fduuid.uuid);
238
- reactor_on_close(old_data.fduuid.uuid);
239
- }
240
- }
241
-
242
- /**
243
- Destroys the library data.
244
-
245
- Call this function before calling any `libsock` functions.
246
- */
247
- static void destroy_lib_data(void) {
248
- if (fd_info) {
249
- while (fd_capacity--) { // include 0 in countdown
250
- // if (fd_info[fd_capacity].open) {
251
- // fprintf(stderr, "Socket %lu is marked as open\n", fd_capacity);
252
- // }
253
- set_fd(fd_capacity, LIB_SOCK_STATE_CLOSED);
254
- }
255
- #if USE_MALLOC == 1
256
- free(fd_info);
257
- #else
258
- munmap(fd_info,
259
- (BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL) +
260
- (sizeof(fd_info_s) * fd_capacity));
261
- #endif
262
- }
263
- fd_info = NULL;
264
- buffer_pool.pool = NULL;
265
- buffer_pool.allocated = NULL;
266
- buffer_pool.lock = SPN_LOCK_INIT;
267
- }
268
-
269
- /**
270
- Initializes the library.
271
-
272
- Call this function before calling any `libsock` functions.
273
- */
274
- static void sock_lib_init(void) {
275
- if (fd_info)
276
- return;
277
-
278
- fd_capacity = sock_max_capacity();
279
- size_t fd_map_mem_size = sizeof(fd_info_s) * fd_capacity;
280
- size_t buffer_mem_size = BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL;
281
-
282
- void *buff_mem;
283
- #if USE_MALLOC == 1
284
- buff_mem = malloc(fd_map_mem_size + buffer_mem_size);
285
- if (buff_mem == NULL) {
286
- perror("Couldn't initialize libsock - not enough memory? ");
287
- exit(1);
288
- }
289
- #else
290
- buff_mem = mmap(NULL, fd_map_mem_size + buffer_mem_size,
291
- PROT_READ | PROT_WRITE | PROT_EXEC,
292
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
293
- // MAP_SHARED | MAP_ANONYMOUS, -1, 0);
294
- if (buff_mem == MAP_FAILED || buff_mem == NULL) {
295
- perror("Couldn't initialize libsock - not enough memory? ");
296
- exit(1);
297
- }
298
- #endif
299
- fd_info = buff_mem;
300
- for (size_t i = 0; i < fd_capacity; i++) {
301
- fd_info[i] = (fd_info_s){.lock = SPN_LOCK_INIT};
302
- spn_unlock(&fd_info[i].lock);
303
- }
304
- /* initialize pool */
305
- buffer_pool.allocated = (void *)((uintptr_t)buff_mem + fd_map_mem_size);
306
- buffer_pool.pool = buffer_pool.allocated;
307
- sock_packet_s *pos = buffer_pool.pool;
308
- for (size_t i = 0; i < BUFFER_PACKET_POOL - 1; i++) {
309
- *pos = (sock_packet_s){
310
- .metadata.next = (void *)(((uintptr_t)pos) + BUFFER_PACKET_REAL_SIZE),
311
- };
312
- pos = pos->metadata.next;
313
- }
314
- pos->metadata.next = 0;
315
- /* deallocate and manage on exit */
316
- atexit(destroy_lib_data);
317
- #ifdef DEBUG
318
- fprintf(stderr, "\nInitialized libsock for %lu sockets, "
319
- "each one requires %lu bytes.\n"
320
- "overall ovearhead: %lu bytes.\n"
321
- "Initialized packet pool for %d elements, "
322
- "each one %lu bytes.\n"
323
- "overall buffer ovearhead: %lu bytes.\n"
324
- "=== Total: %lu bytes ===\n\n",
325
- fd_capacity, sizeof(*fd_info), sizeof(*fd_info) * fd_capacity,
326
- BUFFER_PACKET_POOL, BUFFER_PACKET_REAL_SIZE,
327
- BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL,
328
- (BUFFER_PACKET_REAL_SIZE * BUFFER_PACKET_POOL) +
329
- (sizeof(*fd_info) * fd_capacity));
330
- #endif
331
- }
332
-
333
- #define review_lib() \
334
- if (fd_info == NULL) \
335
- sock_lib_init();
336
-
337
- /* *****************************************************************************
338
- Read / Write internals
339
- */
340
-
341
- #define ERR_OK (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN)
342
- #define ERR_TRY_AGAIN (errno == EINTR)
343
-
344
- static inline int sock_flush_fd_failed(int fd) {
345
- sock_free_packet(fd_info[fd].packet);
346
- fd_info[fd].packet = NULL;
347
- fd_info[fd].close = 1;
348
- fd_info[fd].err = 1;
349
- return 0;
350
- }
351
-
352
- #if USE_SENDFILE == 1
353
-
354
- #if defined(__linux__) /* linux sendfile API */
355
- static inline int sock_flush_os_sendfile(int fd) {
356
- ssize_t sent;
357
- sock_packet_s *packet = fd_info[fd].packet;
358
- sent =
359
- sendfile64(fd, (int)((ssize_t)packet->buffer), &packet->metadata.offset,
360
- packet->length - fd_info[fd].sent);
361
-
362
- if (sent < 0) {
363
- if (ERR_OK)
364
- return -1;
365
- else if (ERR_TRY_AGAIN)
366
- return 0;
367
- else
368
- return sock_flush_fd_failed(fd);
369
- }
370
- if (sent == 0)
371
- fd_info[fd].sent = packet->length;
372
- fd_info[fd].sent += sent;
373
- return 0;
374
- }
375
-
376
- #elif defined(__APPLE__) || defined(__unix__) /* BSD / Apple API */
377
-
378
- static inline int sock_flush_os_sendfile(int fd) {
379
- off_t act_sent;
380
- sock_packet_s *packet = fd_info[fd].packet;
381
- act_sent = packet->length - fd_info[fd].sent;
382
-
383
- #if defined(__APPLE__)
384
- if (sendfile((int)((ssize_t)packet->buffer), fd, packet->metadata.offset,
385
- &act_sent, NULL, 0) < 0 &&
386
- act_sent == 0)
387
- #else
388
- if (sendfile((int)((ssize_t)packet->buffer), fd, packet->metadata.offset,
389
- (size_t)act_sent, NULL, &act_sent, 0) < 0 &&
390
- act_sent == 0)
391
- #endif
392
- {
393
- if (ERR_OK)
394
- return -1;
395
- else if (ERR_TRY_AGAIN)
396
- return 0;
397
- else
398
- return sock_flush_fd_failed(fd);
399
- }
400
- if (act_sent == 0) {
401
- fd_info[fd].sent = packet->length;
402
- return 0;
403
- }
404
- packet->metadata.offset += act_sent;
405
- fd_info[fd].sent += act_sent;
406
- return 0;
407
- }
408
- #endif
409
-
410
- #else
411
-
412
- static inline int sock_flush_os_sendfile(int fd) { return -1; }
413
-
414
- #endif
415
-
416
- static inline int sock_flush_fd(int fd) {
417
- if (USE_SENDFILE && fd_info[fd].rw_hooks == NULL)
418
- return sock_flush_os_sendfile(fd);
419
- ssize_t sent;
420
- sock_packet_s *packet = fd_info[fd].packet;
421
- // how much data are we expecting to send...?
422
- ssize_t i_exp = (BUFFER_PACKET_SIZE > packet->length) ? packet->length
423
- : BUFFER_PACKET_SIZE;
424
-
425
- // read data into the internal buffer
426
- if (packet->metadata.internal_flag == 0) {
427
- ssize_t i_read;
428
- i_read = pread((int)((ssize_t)packet->buffer), packet + 1, i_exp,
429
- packet->metadata.offset);
430
- if (i_read <= 0) {
431
- fd_info[fd].sent = fd_info[fd].packet->length;
432
- return 0;
433
- } else {
434
- packet->metadata.offset += i_read;
435
- packet->metadata.internal_flag = 1;
436
- }
437
- }
438
- // send the data
439
- if (fd_info[fd].rw_hooks && fd_info[fd].rw_hooks->write)
440
- sent = fd_info[fd].rw_hooks->write(
441
- fd_info[fd].fduuid.uuid,
442
- (void *)(((uintptr_t)(packet + 1)) + fd_info[fd].sent),
443
- i_exp - fd_info[fd].sent);
444
- else
445
- sent = write(fd, (void *)(((uintptr_t)(packet + 1)) + fd_info[fd].sent),
446
- i_exp - fd_info[fd].sent);
447
- // review result and update packet data
448
- if (sent < 0) {
449
- if (ERR_OK)
450
- return -1;
451
- else if (ERR_TRY_AGAIN)
452
- return 0;
453
- else
454
- return sock_flush_fd_failed(fd);
455
- }
456
- fd_info[fd].sent += sent;
457
- if (fd_info[fd].sent >= i_exp) {
458
- packet->metadata.internal_flag = 0;
459
- fd_info[fd].sent = 0;
460
- packet->length -= i_exp;
461
- }
462
- return 0;
463
- }
464
-
465
- static inline int sock_flush_data(int fd) {
466
- ssize_t sent;
467
- if (fd_info[fd].rw_hooks && fd_info[fd].rw_hooks->write)
468
- sent = fd_info[fd].rw_hooks->write(
469
- fd_info[fd].fduuid.uuid,
470
- (void *)((uintptr_t)fd_info[fd].packet->buffer + fd_info[fd].sent),
471
- fd_info[fd].packet->length - fd_info[fd].sent);
472
- else
473
- sent = write(
474
- fd, (void *)((uintptr_t)fd_info[fd].packet->buffer + fd_info[fd].sent),
475
- fd_info[fd].packet->length - fd_info[fd].sent);
476
- if (sent < 0) {
477
- if (ERR_OK)
478
- return -1;
479
- else if (ERR_TRY_AGAIN)
480
- return 0;
481
- else
482
- return sock_flush_fd_failed(fd);
483
- }
484
- fd_info[fd].sent += sent;
485
- return 0;
486
- }
487
-
488
- static void sock_flush_unsafe(int fd) {
489
- while (fd_info[fd].packet) {
490
- if (fd_info[fd].packet->metadata.is_fd == 0) {
491
- if (sock_flush_data(fd))
492
- return;
493
- } else {
494
- if (sock_flush_fd(fd))
495
- return;
496
- }
497
- if (fd_info[fd].packet && fd_info[fd].packet->length <= fd_info[fd].sent) {
498
- sock_packet_s *packet = fd_info[fd].packet;
499
- fd_info[fd].packet = packet->metadata.next;
500
- packet->metadata.next = NULL;
501
- fd_info[fd].sent = 0;
502
- sock_free_packet(packet);
503
- }
504
- }
505
- }
506
-
507
- #if SOCK_DELAY_WRITE == 1
508
-
509
- static inline void sock_flush_schd(intptr_t uuid) {
510
- if (async_run((void (*)(void *))sock_flush, (void *)uuid) == -1)
511
- goto fallback;
512
- return;
513
- fallback:
514
- sock_flush_unsafe(sock_uuid2fd(uuid));
515
- }
516
-
517
- #define _write_to_sock() sock_flush_schd(sfd->fduuid.uuid)
518
-
519
- #else
520
-
521
- #define _write_to_sock() sock_flush_unsafe(fd)
522
-
523
- #endif
524
-
525
- static inline void sock_send_packet_unsafe(int fd, sock_packet_s *packet) {
526
- fd_info_s *sfd = fd_info + fd;
527
- if (sfd->packet == NULL) {
528
- /* no queue, nothing to check */
529
- sfd->packet = packet;
530
- _write_to_sock();
531
- return;
532
-
533
- } else if (packet->metadata.urgent == 0) {
534
- /* not urgent, last in line */
535
- sock_packet_s *pos = sfd->packet;
536
- while (pos->metadata.next)
537
- pos = pos->metadata.next;
538
- pos->metadata.next = packet;
539
- _write_to_sock();
540
- return;
541
-
542
- } else {
543
- /* urgent, find a spot we can interrupt */
544
- sock_packet_s **pos = &sfd->packet;
545
- while (*pos && (*pos)->metadata.can_interrupt == 0)
546
- pos = &(*pos)->metadata.next;
547
- sock_packet_s *tail = *pos;
548
- *pos = packet;
549
- if (tail) {
550
- pos = &packet->metadata.next;
551
- while (*pos)
552
- pos = &(*pos)->metadata.next;
553
- *pos = tail;
554
- }
555
- }
556
- _write_to_sock();
557
- }
558
-
559
- /* *****************************************************************************
560
- Listen
561
- */
562
-
563
- /**
564
- Opens a listening non-blocking socket. Return's the socket's UUID.
565
- */
566
- intptr_t sock_listen(const char *address, const char *port) {
567
- review_lib();
568
- int srvfd;
569
- // setup the address
570
- struct addrinfo hints;
571
- struct addrinfo *servinfo; // will point to the results
572
- memset(&hints, 0, sizeof hints); // make sure the struct is empty
573
- hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
574
- hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
575
- hints.ai_flags = AI_PASSIVE; // fill in my IP for me
576
- if (getaddrinfo(address, port, &hints, &servinfo)) {
577
- // perror("addr err");
578
- return -1;
579
- }
580
- // get the file descriptor
581
- srvfd =
582
- socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
583
- if (srvfd <= 0) {
584
- // perror("socket err");
585
- freeaddrinfo(servinfo);
586
- return -1;
587
- }
588
- // make sure the socket is non-blocking
589
- if (sock_set_non_block(srvfd) < 0) {
590
- // perror("couldn't set socket as non blocking! ");
591
- freeaddrinfo(servinfo);
592
- close(srvfd);
593
- return -1;
594
- }
595
- // avoid the "address taken"
596
- {
597
- int optval = 1;
598
- setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
599
- }
600
- // bind the address to the socket
601
- {
602
- int bound = 0;
603
- for (struct addrinfo *p = servinfo; p != NULL; p = p->ai_next) {
604
- if (!bind(srvfd, p->ai_addr, p->ai_addrlen))
605
- bound = 1;
606
- }
607
-
608
- if (!bound) {
609
- // perror("bind err");
610
- freeaddrinfo(servinfo);
611
- close(srvfd);
612
- return -1;
613
- }
614
- }
615
- freeaddrinfo(servinfo);
616
- // listen in
617
- if (listen(srvfd, SOMAXCONN) < 0) {
618
- // perror("couldn't start listening");
619
- close(srvfd);
620
- return -1;
621
- }
622
- set_fd(srvfd, LIB_SOCK_STATE_OPEN);
623
- return fd_info[srvfd].fduuid.uuid;
624
- }
625
-
626
- /* *****************************************************************************
627
- Accept
628
- */
629
-
630
- intptr_t sock_accept(intptr_t srv_uuid) {
631
- review_lib();
632
- static socklen_t cl_addrlen = 0;
633
- int client;
634
- #ifdef SOCK_NONBLOCK
635
- client = accept4(sock_uuid2fd(srv_uuid), NULL, &cl_addrlen, SOCK_NONBLOCK);
636
- if (client <= 0)
637
- return -1;
638
- #else
639
- client = accept(sock_uuid2fd(srv_uuid), NULL, &cl_addrlen);
640
- if (client <= 0)
641
- return -1;
642
- sock_set_non_block(client);
643
- #endif
644
- set_fd(client, LIB_SOCK_STATE_OPEN);
645
- return fd_info[client].fduuid.uuid;
646
- }
647
-
648
- /* *****************************************************************************
649
- Connect
650
- */
651
- intptr_t sock_connect(char *address, char *port) {
652
- review_lib();
653
- int fd;
654
- // setup the address
655
- struct addrinfo hints;
656
- struct addrinfo *addrinfo; // will point to the results
657
- memset(&hints, 0, sizeof hints); // make sure the struct is empty
658
- hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
659
- hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
660
- hints.ai_flags = AI_PASSIVE; // fill in my IP for me
661
- if (getaddrinfo(address, port, &hints, &addrinfo)) {
662
- return -1;
663
- }
664
- // get the file descriptor
665
- fd =
666
- socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
667
- if (fd <= 0) {
668
- freeaddrinfo(addrinfo);
669
- return -1;
670
- }
671
- // make sure the socket is non-blocking
672
- if (sock_set_non_block(fd) < 0) {
673
- freeaddrinfo(addrinfo);
674
- close(fd);
675
- return -1;
676
- }
677
-
678
- if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) < 0 &&
679
- errno != EINPROGRESS) {
680
- close(fd);
681
- freeaddrinfo(addrinfo);
682
- return -1;
683
- }
684
- freeaddrinfo(addrinfo);
685
- set_fd(fd, LIB_SOCK_STATE_OPEN);
686
- return fd_info[fd].fduuid.uuid;
687
- }
688
-
689
- /* *****************************************************************************
690
- Open existing
691
- */
692
-
693
- intptr_t sock_open(int fd) {
694
- review_lib();
695
- set_fd(fd, LIB_SOCK_STATE_OPEN);
696
- return fd_info[fd].fduuid.uuid;
697
- }
698
-
699
- /* *****************************************************************************
700
- Information about the socket
701
- */
702
-
703
- /**
704
- Returns 1 if the uuid refers to a valid and open, socket.
705
-
706
- Returns 0 if not.
707
- */
708
- int sock_isvalid(intptr_t uuid) { return fd_info && is_valid(uuid); }
709
-
710
- /**
711
- `sock_fd2uuid` takes an existing file decriptor `fd` and returns it's active
712
- `uuid`.
713
- */
714
- intptr_t sock_fd2uuid(int fd) {
715
- return (fd_info && fd_info[fd].open) ? fd_info[fd].fduuid.uuid : -1;
716
- }
717
-
718
- /* *****************************************************************************
719
- Buffer API.
720
- */
721
-
722
- static inline sock_packet_s *sock_try_checkout_packet(void) {
723
- sock_packet_s *packet;
724
- spn_lock(&buffer_pool.lock);
725
- packet = buffer_pool.pool;
726
- if (packet) {
727
- buffer_pool.pool = packet->metadata.next;
728
- spn_unlock(&buffer_pool.lock);
729
- *packet = (sock_packet_s){.buffer = packet + 1, .metadata.next = NULL};
730
- return packet;
731
- }
732
- spn_unlock(&buffer_pool.lock);
733
- return packet;
734
- }
735
-
736
- /**
737
- Checks out a `sock_packet_s` from the packet pool, transfering the
738
- ownership of the memory to the calling function. The function will hang until
739
- a
740
- packet becomes available, so never check out more then a single packet at a
741
- time.
742
- */
743
- sock_packet_s *sock_checkout_packet(void) {
744
- review_lib();
745
- sock_packet_s *packet = NULL;
746
- for (;;) {
747
- spn_lock(&buffer_pool.lock);
748
- packet = buffer_pool.pool;
749
- if (packet) {
750
- buffer_pool.pool = packet->metadata.next;
751
- spn_unlock(&buffer_pool.lock);
752
- *packet = (sock_packet_s){
753
- .buffer = packet + 1, .metadata.next = NULL, .metadata.dealloc = free,
754
- };
755
- return packet;
756
- }
757
- spn_unlock(&buffer_pool.lock);
758
- reschedule_thread();
759
- sock_flush_all();
760
- }
761
- }
762
- /**
763
- Attaches a packet to a socket's output buffer and calls `sock_flush` for the
764
- socket.
765
- */
766
- ssize_t sock_send_packet(intptr_t uuid, sock_packet_s *packet) {
767
- if (!fd_info || !is_valid(uuid)) {
768
- sock_free_packet(packet);
769
- return -1;
770
- }
771
- spn_lock(&uuid2info(uuid).lock);
772
- sock_send_packet_unsafe(sock_uuid2fd(uuid), packet);
773
- spn_unlock(&uuid2info(uuid).lock);
774
- return 0;
775
- }
776
-
777
- /**
778
- Returns TRUE (non 0) if there is data waiting to be written to the socket in
779
- the
780
- user-land buffer.
781
- */
782
- int sock_packets_pending(intptr_t uuid) {
783
- return fd_info && uuid2info(uuid).packet != NULL;
784
- }
785
-
786
- /**
787
- Use `sock_free_packet` to free unused packets that were checked-out using
788
- `sock_checkout_packet`.
789
- */
790
- void sock_free_packet(sock_packet_s *packet) {
791
- sock_packet_s *next = packet;
792
- if (packet == NULL)
793
- return;
794
- for (;;) {
795
- if (next->metadata.is_fd) {
796
- if (next->metadata.keep_open == 0)
797
- close((int)((ssize_t)next->buffer));
798
- } else if (next->metadata.external)
799
- next->metadata.dealloc(next->buffer);
800
- if (next->metadata.next == NULL)
801
- break; /* next will hold the last packet in the chain. */
802
- next = next->metadata.next;
803
- }
804
- spn_lock(&buffer_pool.lock);
805
- next->metadata.next = buffer_pool.pool;
806
- buffer_pool.pool = packet;
807
- spn_unlock(&buffer_pool.lock);
808
- }
809
-
810
- /* *****************************************************************************
811
- Reading
812
- */
813
- ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
814
- if (!fd_info || !is_valid(uuid)) {
815
- errno = ENODEV;
816
- return -1;
817
- }
818
- ssize_t i_read;
819
- fd_info_s *sfd = fd_info + sock_uuid2fd(uuid);
820
- if (sfd->rw_hooks && sfd->rw_hooks->read)
821
- i_read = sfd->rw_hooks->read(uuid, buf, count);
822
- else
823
- i_read = read(sock_uuid2fd(uuid), buf, count);
824
-
825
- if (i_read > 0) {
826
- sock_touch(uuid);
827
- return i_read;
828
- }
829
- if (i_read == -1 && (ERR_OK || ERR_TRY_AGAIN))
830
- return 0;
831
- // fprintf(stderr, "Read Error for %lu bytes from fd %d (closing))\n",
832
- // count,
833
- // sock_uuid2fd(uuid));
834
- sock_close(uuid);
835
- return -1;
836
- }
837
-
838
- /* *****************************************************************************
839
- Flushing
840
- */
841
-
842
- ssize_t sock_flush(intptr_t uuid) {
843
- if (!fd_info || !is_valid(uuid))
844
- return -1;
845
- if (uuid2info(uuid).packet == NULL)
846
- goto no_packet;
847
- spn_lock(&uuid2info(uuid).lock);
848
- sock_flush_unsafe(sock_uuid2fd(uuid));
849
- spn_unlock(&uuid2info(uuid).lock);
850
- no_packet:
851
- if (uuid2info(uuid).close) {
852
- sock_force_close(uuid);
853
- return -1;
854
- }
855
- return 0;
856
- }
857
- /**
858
- `sock_flush_strong` performs the same action as `sock_flush` but returns only
859
- after all the data was sent. This is an "active" wait, polling isn't
860
- performed.
861
- */
862
- void sock_flush_strong(intptr_t uuid) {
863
- if (!fd_info)
864
- return;
865
- while (is_valid(uuid) && uuid2info(uuid).packet)
866
- sock_flush(uuid);
867
- }
868
- /**
869
- Calls `sock_flush` for each file descriptor that's buffer isn't empty.
870
- */
871
- void sock_flush_all(void) {
872
- for (size_t i = 0; i < fd_capacity; i++) {
873
- if (fd_info[i].packet == NULL || spn_is_locked(&fd_info[i].lock))
874
- continue;
875
- sock_flush(fd_info[i].fduuid.uuid);
876
- }
877
- }
878
-
879
- /* *****************************************************************************
880
- Writing
881
- */
882
-
883
- ssize_t sock_write2_fn(sock_write_info_s options) {
884
- if (!fd_info || !is_valid(options.fduuid)) {
885
- errno = ENODEV;
886
- return -1;
887
- }
888
- if (options.buffer == NULL)
889
- return -1;
890
- if (!options.length && !options.is_fd)
891
- options.length = strlen(options.buffer);
892
- if (options.length == 0)
893
- return -1;
894
- sock_packet_s *packet = sock_checkout_packet();
895
- packet->metadata.can_interrupt = 1;
896
- packet->metadata.urgent = options.urgent;
897
-
898
- if (options.is_fd) {
899
- packet->buffer = (void *)options.buffer;
900
- packet->length = options.length;
901
- packet->metadata.is_fd = options.is_fd;
902
- packet->metadata.offset = options.offset;
903
- return sock_send_packet(options.fduuid, packet);
904
- } else {
905
- if (options.move) {
906
- packet->buffer = (void *)options.buffer;
907
- packet->length = options.length;
908
- packet->metadata.external = 1;
909
- return sock_send_packet(options.fduuid, packet);
910
- } else {
911
- if (options.length <= BUFFER_PACKET_SIZE) {
912
- memcpy(packet->buffer, options.buffer, options.length);
913
- packet->length = options.length;
914
- return sock_send_packet(options.fduuid, packet);
915
- } else {
916
- if (packet->metadata.urgent) {
917
- fprintf(stderr, "Socket err:"
918
- "Large data cannot be sent as an urgent packet.\n"
919
- "Urgency silently ignored\n");
920
- packet->metadata.urgent = 0;
921
- }
922
- size_t to_cpy;
923
- spn_lock(&uuid2info(options.fduuid).lock);
924
- for (;;) {
925
- to_cpy = options.length > BUFFER_PACKET_SIZE ? BUFFER_PACKET_SIZE
926
- : options.length;
927
- memcpy(packet->buffer, options.buffer, to_cpy);
928
- packet->length = to_cpy;
929
- options.length -= to_cpy;
930
- options.buffer = (void *)((uintptr_t)options.buffer + to_cpy);
931
- sock_send_packet_unsafe(sock_uuid2fd(options.fduuid), packet);
932
- if (!is_valid(options.fduuid) || uuid2info(options.fduuid).err == 1 ||
933
- options.length == 0)
934
- break;
935
- packet = sock_try_checkout_packet();
936
- while (packet == NULL) {
937
- sock_flush_all();
938
- sock_flush_unsafe(sock_uuid2fd(options.fduuid));
939
- packet = sock_try_checkout_packet();
940
- }
941
- }
942
- spn_unlock(&uuid2info(options.fduuid).lock);
943
- if (uuid2info(options.fduuid).packet == NULL &&
944
- uuid2info(options.fduuid).close) {
945
- sock_force_close(options.fduuid);
946
- return -1;
947
- }
948
- return is_valid(options.fduuid) ? 0 : -1;
949
- }
950
- }
951
- }
952
- // how did we get here?
953
- return -1;
954
- }
955
-
956
- /* *****************************************************************************
957
- Closing.
958
- */
959
-
960
- void sock_close(intptr_t uuid) {
961
- // fprintf(stderr, "called sock_close for %lu (%d)\n", uuid,
962
- // sock_uuid2fd(uuid));
963
- if (!fd_info || !is_valid(uuid))
964
- return;
965
- fd_info[sock_uuid2fd(uuid)].close = 1;
966
- sock_flush(uuid);
967
- }
968
-
969
- void sock_force_close(intptr_t uuid) {
970
- // fprintf(stderr, "called sock_force_close for %lu (%d)\n", uuid,
971
- // sock_uuid2fd(uuid));
972
- if (!fd_info || !is_valid(uuid))
973
- return;
974
- shutdown(sock_uuid2fd(uuid), SHUT_RDWR);
975
- close(sock_uuid2fd(uuid));
976
- set_fd(sock_uuid2fd(uuid), LIB_SOCK_STATE_CLOSED);
977
- }
978
-
979
- /* *****************************************************************************
980
- RW hooks implementation
981
- */
982
-
983
- /** Gets a socket hook state (a pointer to the struct). */
984
- struct sock_rw_hook_s *sock_rw_hook_get(intptr_t uuid) {
985
- if (!fd_info || !is_valid(uuid))
986
- return NULL;
987
- return uuid2info(uuid).rw_hooks;
988
- }
989
-
990
- /** Sets a socket hook state (a pointer to the struct). */
991
- int sock_rw_hook_set(intptr_t uuid, sock_rw_hook_s *rw_hooks) {
992
- if (!fd_info || !is_valid(uuid))
993
- return -1;
994
- spn_lock(&(uuid2info(uuid).lock));
995
- uuid2info(uuid).rw_hooks = rw_hooks;
996
- spn_unlock(&uuid2info(uuid).lock);
997
- return 0;
998
- }
999
-
1000
- /* *****************************************************************************
1001
- test
1002
- */
1003
- #ifdef DEBUG
1004
- void sock_libtest(void) {
1005
- sock_lib_init();
1006
- sock_packet_s *p, *pl;
1007
- size_t count = 0;
1008
- fprintf(stderr, "Testing packet pool\n");
1009
- for (size_t i = 0; i < BUFFER_PACKET_POOL * 2; i++) {
1010
- count = 1;
1011
- pl = p = sock_checkout_packet();
1012
- while (buffer_pool.pool) {
1013
- count++;
1014
- pl->metadata.next = sock_checkout_packet();
1015
- pl = pl->metadata.next;
1016
- }
1017
- sock_free_packet(p);
1018
- // fprintf(stderr, "Collected and freed %lu packets.\n", count);
1019
- }
1020
- fprintf(stderr,
1021
- "liniar (no-contention) packet checkout + free shows %lu packets. "
1022
- "test %s\n",
1023
- count, count == BUFFER_PACKET_POOL ? "passed." : "FAILED!");
1024
- }
1025
- #endif