iodine 0.4.19 → 0.5.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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
@@ -1,21 +1,14 @@
1
1
  /*
2
- Copyright: Boaz segev, 2017
3
- License: MIT except for any non-public-domain algorithms (none that I'm aware
4
- of), which might be subject to their own licenses.
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
5
4
 
6
- Feel free to copy, use and enjoy in accordance with to the license(s).
5
+ Feel free to copy, use and enjoy according to the license provided.
7
6
  */
8
7
  #ifndef H_REDIS_ENGINE_H
9
- /**
10
- This is a simple, optimistic Redis engine that matches the requirements of
11
- facil.io's pub/sub engine design.
12
-
13
- The engine is optimistic, meanning the engine will never report a failed
14
- subscription or publication... it will simply try until successful.
15
- */
16
8
  #define H_REDIS_ENGINE_H
9
+
10
+ #include "fiobj.h"
17
11
  #include "pubsub.h"
18
- #include "resp.h"
19
12
 
20
13
  /* support C++ */
21
14
  #ifdef __cplusplus
@@ -37,42 +30,52 @@ struct redis_engine_create_args {
37
30
  };
38
31
 
39
32
  /**
40
- See the {pubsub.h} file for documentation about engines.
41
-
42
- The engine is active only after facil.io starts running.
43
-
44
- A `ping` will be sent every `ping_interval` interval or inactivity. The default
45
- value (0) will fallback to facil.io's maximum time of inactivity (5 minutes)
46
- before polling on the connection's protocol.
47
-
48
- function names speak for themselves ;-)
49
- */
33
+ * See the {pubsub.h} file for documentation about engines.
34
+ *
35
+ * The engine is active only after facil.io starts running.
36
+ *
37
+ * A `ping` will be sent every `ping_interval` interval or inactivity. The
38
+ * default value (0) will fallback to facil.io's maximum time of inactivity (5
39
+ * minutes) before polling on the connection's protocol.
40
+ *
41
+ * function names speak for themselves ;-)
42
+ *
43
+ * Note: The Redis engine assumes it will stay alive until all the messages and
44
+ * callbacks have been called (or facil.io exits)... If the engine is destroyed
45
+ * midway, memory leaks might occur (and little puppies might cry).
46
+ */
50
47
  pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
51
-
52
48
  #define redis_engine_create(...) \
53
49
  redis_engine_create((struct redis_engine_create_args){__VA_ARGS__})
54
50
 
55
51
  /**
56
- Sends a Redis message through the engine's connection. The response will be sent
57
- back using the optional callback. `udata` is passed along untouched.
58
-
59
- The message will be repeated endlessly until a response validates the fact that
60
- it was sent (or the engine is destroyed).
61
- */
62
- intptr_t redis_engine_send(pubsub_engine_s *engine, resp_object_s *data,
63
- void (*callback)(pubsub_engine_s *e,
64
- resp_object_s *reply, void *udata),
52
+ * Sends a Redis command through the engine's connection.
53
+ *
54
+ * The response will be sent back using the optional callback. `udata` is passed
55
+ * along untouched.
56
+ *
57
+ * The message will be resent on network failures, until a response validates
58
+ * the fact that the command was sent (or the engine is destroyed).
59
+ *
60
+ * Note: NEVER call Pub/Sub commands using this function, as it will violate the
61
+ * Redis connection's protocol (best case scenario, a disconnection will occur
62
+ * before and messages are lost).
63
+ */
64
+ intptr_t redis_engine_send(pubsub_engine_s *engine, FIOBJ command, FIOBJ data,
65
+ void (*callback)(pubsub_engine_s *e, FIOBJ reply,
66
+ void *udata),
65
67
  void *udata);
66
68
 
67
69
  /**
68
- See the {pubsub.h} file for documentation about engines.
69
-
70
- function names speak for themselves ;-)
71
- */
72
- void redis_engine_destroy(const pubsub_engine_s *engine);
70
+ * See the {pubsub.h} file for documentation about engines.
71
+ *
72
+ * function names speak for themselves ;-)
73
+ */
74
+ void redis_engine_destroy(pubsub_engine_s *engine);
73
75
 
76
+ /* support C++ */
74
77
  #ifdef __cplusplus
75
- } /* extern "C" */
78
+ }
76
79
  #endif
77
80
 
78
- #endif
81
+ #endif /* H_REDIS_ENGINE_H */
@@ -0,0 +1,311 @@
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_RESP_PARSER_H
8
+ /**
9
+ * This single file library is a RESP parser for Redis connections.
10
+ *
11
+ * To use this file, the `.c` file in which this file is included MUST define a
12
+ * number of callbacks, as later inticated.
13
+ *
14
+ * When feeding the parser, the parser will inform of any trailing bytes (bytes
15
+ * at the end of the buffer that could not be parsed). These bytes should be
16
+ * resent to the parser along with more data. Zero is a valid return value.
17
+ *
18
+ * Note: mostly, callback return vaslues are ignored.
19
+ */
20
+ #define H_RESP_PARSER_H
21
+
22
+ #include <stdint.h>
23
+ #include <stdlib.h>
24
+ #include <string.h>
25
+
26
+ /* *****************************************************************************
27
+ The Parser
28
+ ***************************************************************************** */
29
+
30
+ typedef struct resp_parser_s {
31
+ /* for internal use - (array / object countdown) */
32
+ intptr_t obj_countdown;
33
+ /* for internal use - (string byte consumption) */
34
+ intptr_t expecting;
35
+ } resp_parser_s;
36
+
37
+ /**
38
+ * Returns the number of bytes to be resent. i.e., for a return value 5, the
39
+ * last 5 bytes in the buffer need to be resent to the parser.
40
+ */
41
+ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
42
+ size_t length);
43
+
44
+ /* *****************************************************************************
45
+ Required Parser Callbacks (to be defined by the including file)
46
+ ***************************************************************************** */
47
+
48
+ /** a local static callback, called when the RESP message is complete. */
49
+ static int resp_on_message(resp_parser_s *parser);
50
+
51
+ /** a local static callback, called when a Number object is parsed. */
52
+ static int resp_on_number(resp_parser_s *parser, int64_t num);
53
+ /** a local static callback, called when a OK message is received. */
54
+ static int resp_on_okay(resp_parser_s *parser);
55
+ /** a local static callback, called when NULL is received. */
56
+ static int resp_on_null(resp_parser_s *parser);
57
+
58
+ /**
59
+ * a local static callback, called when a String should be allocated.
60
+ *
61
+ * `str_len` is the expected number of bytes that will fill the final string
62
+ * object, without any NUL byte marker (the string might be binary).
63
+ *
64
+ * If this function returns any value besides 0, parsing is stopped.
65
+ */
66
+ static int resp_on_start_string(resp_parser_s *parser, size_t str_len);
67
+ /** a local static callback, called as String objects are streamed. */
68
+ static int resp_on_string_chunk(resp_parser_s *parser, void *data, size_t len);
69
+ /** a local static callback, called when a String object had finished streaming.
70
+ */
71
+ static int resp_on_end_string(resp_parser_s *parser);
72
+
73
+ /** a local static callback, called an error message is received. */
74
+ static int resp_on_err_msg(resp_parser_s *parser, void *data, size_t len);
75
+
76
+ /**
77
+ * a local static callback, called when an Array should be allocated.
78
+ *
79
+ * `array_len` is the expected number of objects that will fill the Array
80
+ * object.
81
+ *
82
+ * There's no `resp_on_end_array` callback since the RESP protocol assumes the
83
+ * message is finished along with the Array (`resp_on_message` is called).
84
+ * However, just in case a non-conforming client/server sends nested Arrays, the
85
+ * callback should test against possible overflow or nested Array endings.
86
+ *
87
+ * If this function returns any value besides 0, parsing is stopped.
88
+ */
89
+ static int resp_on_start_array(resp_parser_s *parser, size_t array_len);
90
+
91
+ /** a local static callback, called when a parser / protocol error occurs. */
92
+ static int resp_on_parser_error(resp_parser_s *parser);
93
+
94
+ /* *****************************************************************************
95
+ Seeking the new line...
96
+ ***************************************************************************** */
97
+
98
+ #if FIO_MEMCHAR
99
+
100
+ /**
101
+ * This seems to be faster on some systems, especially for smaller distances.
102
+ *
103
+ * On newer systems, `memchr` should be faster.
104
+ */
105
+ static inline int seek2ch(uint8_t **buffer, register const uint8_t *limit,
106
+ const uint8_t c) {
107
+ if (**buffer == c)
108
+ return 1;
109
+
110
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
111
+ /* too short for this mess */
112
+ if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
113
+ goto finish;
114
+
115
+ /* align memory */
116
+ {
117
+ const uint8_t *alignment =
118
+ (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
119
+ if (limit >= alignment) {
120
+ while (*buffer < alignment) {
121
+ if (**buffer == c)
122
+ return 1;
123
+ *buffer += 1;
124
+ }
125
+ }
126
+ }
127
+ const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
128
+ #else
129
+ const uint8_t *limit64 = (uint8_t *)limit - 7;
130
+ #endif
131
+ uint64_t wanted1 = 0x0101010101010101ULL * c;
132
+ for (; *buffer < limit64; *buffer += 8) {
133
+ const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
134
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
135
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
136
+ if ((t0 & t1)) {
137
+ break;
138
+ }
139
+ }
140
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
141
+ finish:
142
+ #endif
143
+ while (*buffer < limit) {
144
+ if (**buffer == c)
145
+ return 1;
146
+ (*buffer)++;
147
+ }
148
+ return 0;
149
+ }
150
+
151
+ #else
152
+
153
+ /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
154
+ inline static uint8_t seek2ch(uint8_t **pos, const uint8_t *limit, uint8_t ch) {
155
+ /* This is library based alternative that is sometimes slower */
156
+ if (*pos >= limit || **pos == ch) {
157
+ return 0;
158
+ }
159
+ uint8_t *tmp = (uint8_t *)memchr(*pos, ch, limit - (*pos));
160
+ if (tmp) {
161
+ *pos = tmp;
162
+ return 1;
163
+ }
164
+ *pos = (uint8_t *)limit;
165
+ return 0;
166
+ }
167
+
168
+ #endif
169
+
170
+ /* *****************************************************************************
171
+ Parsing RESP requests
172
+ ***************************************************************************** */
173
+
174
+ /**
175
+ * Returns the number of bytes to be resent. i.e., for a return value 5, the
176
+ * last 5 bytes in the buffer need to be resent to the parser.
177
+ */
178
+ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
179
+ size_t length) {
180
+ uint8_t *pos = (uint8_t *)buffer;
181
+ const uint8_t *stop = pos + length;
182
+ while (pos < stop) {
183
+ uint8_t *eol;
184
+ if (parser->expecting) {
185
+ if (pos + parser->expecting + 2 > stop) {
186
+ /* read, but make sure the buffer includes the new line markers */
187
+ size_t tmp = (size_t)((uintptr_t)stop - (uintptr_t)pos);
188
+ if ((intptr_t)tmp >= parser->expecting)
189
+ tmp = parser->expecting - 1;
190
+ resp_on_string_chunk(parser, (void *)pos, tmp);
191
+ parser->expecting -= tmp;
192
+ return (size_t)((uintptr_t)stop - ((uintptr_t)pos + tmp)); /* 0 or 1 */
193
+ } else {
194
+ resp_on_string_chunk(parser, (void *)pos, parser->expecting);
195
+ resp_on_end_string(parser);
196
+ pos += parser->expecting;
197
+ if (pos[0] == '\r')
198
+ ++pos;
199
+ if (pos[0] == '\n')
200
+ ++pos;
201
+ parser->expecting = 0;
202
+ --parser->obj_countdown;
203
+ if (parser->obj_countdown <= 0) {
204
+ parser->obj_countdown = 0;
205
+ if (resp_on_message(parser))
206
+ goto finish;
207
+ }
208
+ continue;
209
+ }
210
+ }
211
+ eol = pos;
212
+ if (seek2ch(&eol, stop, '\n') == 0)
213
+ break;
214
+ switch (*pos) {
215
+ case '+':
216
+ if (pos[1] == 'O' && pos[2] == 'K' && pos[3] == '\r' && pos[4] == '\n') {
217
+ resp_on_okay(parser);
218
+ --parser->obj_countdown;
219
+ break;
220
+ }
221
+ if (resp_on_start_string(parser,
222
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 2))) {
223
+ pos = eol + 1;
224
+ goto finish;
225
+ }
226
+ resp_on_string_chunk(parser, (void *)(pos + 1),
227
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 2));
228
+ resp_on_end_string(parser);
229
+ --parser->obj_countdown;
230
+ break;
231
+ case '-':
232
+ resp_on_err_msg(parser, pos,
233
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 2));
234
+ --parser->obj_countdown;
235
+ break;
236
+ case '*': /* fallthrough */
237
+ case '$': /* fallthrough */
238
+ case ':': {
239
+ uint8_t id = *pos;
240
+ uint8_t inv = 0;
241
+ int64_t i = 0;
242
+ ++pos;
243
+ if (pos[0] == '-') {
244
+ inv = 1;
245
+ ++pos;
246
+ }
247
+ while ((size_t)(pos[0] - (uint8_t)'0') <= 9) {
248
+ i = (i * 10) + (pos[0] - ((uint8_t)'0'));
249
+ ++pos;
250
+ }
251
+ if (inv)
252
+ i = i * -1;
253
+
254
+ switch (id) {
255
+ case ':':
256
+ resp_on_number(parser, i);
257
+ --parser->obj_countdown;
258
+ break;
259
+ case '$':
260
+ if (i < 0) {
261
+ resp_on_null(parser);
262
+ --parser->obj_countdown;
263
+ } else if (i == 0) {
264
+ resp_on_start_string(parser, 0);
265
+ resp_on_end_string(parser);
266
+ --parser->obj_countdown;
267
+ eol += 2; /* consume the extra "\r\n" */
268
+ } else {
269
+ if (resp_on_start_string(parser, i)) {
270
+ pos = eol + 1;
271
+ goto finish;
272
+ }
273
+ parser->expecting = i;
274
+ }
275
+ break;
276
+ case '*':
277
+ if (i < 0) {
278
+ resp_on_null(parser);
279
+ --parser->obj_countdown;
280
+ } else {
281
+ if (resp_on_start_array(parser, i)) {
282
+ pos = eol + 1;
283
+ goto finish;
284
+ }
285
+ parser->obj_countdown += i;
286
+ }
287
+ break;
288
+ }
289
+ } break;
290
+ default:
291
+ if (!parser->obj_countdown && !parser->expecting) {
292
+ /* possible (probable) inline command... for server authoring. */
293
+ /* Not Supported, PRs are welcome. */
294
+ resp_on_parser_error(parser);
295
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
296
+ } else {
297
+ resp_on_parser_error(parser);
298
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
299
+ }
300
+ }
301
+ pos = eol + 1;
302
+ if (parser->obj_countdown <= 0 && !parser->expecting) {
303
+ parser->obj_countdown = 0;
304
+ resp_on_message(parser);
305
+ }
306
+ }
307
+ finish:
308
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
309
+ }
310
+
311
+ #endif /* H_RESP_PARSER_H */
data/ext/iodine/sock.c CHANGED
@@ -21,17 +21,17 @@ Includes and state
21
21
  #include <netinet/in.h>
22
22
  #include <netinet/tcp.h>
23
23
  #include <stdio.h>
24
+ #include <stdlib.h>
24
25
  #include <string.h>
25
- #include <sys/mman.h>
26
+ #include <sys/ioctl.h>
26
27
  #include <sys/resource.h>
27
28
  #include <sys/socket.h>
28
- #include <sys/time.h>
29
+ #include <sys/stat.h>
30
+ #include <sys/sysctl.h>
29
31
  #include <sys/types.h>
30
- #include <time.h>
32
+ #include <sys/un.h>
31
33
 
32
- #if BUFFER_PACKET_SIZE < (BUFFER_FILE_READ_SIZE + 64)
33
- #error BUFFER_PACKET_POOL must be bigger than BUFFER_FILE_READ_SIZE + 64.
34
- #endif
34
+ #include "fio_mem.h"
35
35
 
36
36
  /* *****************************************************************************
37
37
  OS Sendfile settings.
@@ -76,32 +76,36 @@ int defer(void (*func)(void *, void *), void *arg, void *arg2) {
76
76
  func(arg, arg2);
77
77
  return 0;
78
78
  }
79
- static void sock_flush_defer(void *arg, void *ignored) {
79
+
80
+ #pragma weak sock_flush_defer
81
+ void sock_flush_defer(void *arg, void *ignored) {
80
82
  sock_flush((intptr_t)arg);
81
83
  return;
82
84
  (void)ignored;
83
85
  }
84
- /* *****************************************************************************
85
- Support `evio`.
86
- */
87
-
88
- #pragma weak evio_remove
89
- int evio_remove(intptr_t uuid) {
90
- (void)(uuid);
91
- return -1;
92
- }
93
86
 
94
87
  /* *****************************************************************************
95
88
  User-Land Buffer and Packets
96
89
  ***************************************************************************** */
97
90
 
91
+ #ifndef BUFFER_PACKET_POOL
92
+ /* ~4 pages of memory */
93
+ #define BUFFER_PACKET_POOL (((4096 << 2) - 16) / sizeof(packet_s))
94
+ #endif
95
+
98
96
  typedef struct packet_s {
99
- struct packet_metadata_s {
100
- int (*write_func)(int fd, struct packet_s *packet);
101
- void (*free_func)(struct packet_s *packet);
102
- struct packet_s *next;
103
- } metadata;
104
- sock_buffer_s buffer;
97
+ struct packet_s *next;
98
+ int (*write_func)(int fd, struct packet_s *packet);
99
+ union {
100
+ void (*free_func)(void *);
101
+ void (*close_func)(intptr_t);
102
+ };
103
+ union {
104
+ void *buffer;
105
+ intptr_t fd;
106
+ };
107
+ intptr_t offset;
108
+ uintptr_t length;
105
109
  } packet_s;
106
110
 
107
111
  static struct {
@@ -113,70 +117,69 @@ static struct {
113
117
 
114
118
  void SOCK_DEALLOC_NOOP(void *arg) { (void)arg; }
115
119
 
116
- static inline void sock_packet_clear(packet_s *packet) {
117
- packet->metadata.free_func(packet);
118
- packet->metadata = (struct packet_metadata_s){
119
- .free_func = (void (*)(packet_s *))SOCK_DEALLOC_NOOP};
120
- packet->buffer.len = 0;
120
+ typedef struct func_s { void (*task)(void *); } func_s;
121
+
122
+ static void sock_packet_free_cb(void *task, void *buffer) {
123
+ func_s *t = (void *)&task;
124
+ t->task(buffer);
125
+ }
126
+
127
+ static void sock_packet_free_attempt(void *packet_, void *ignr) {
128
+ if (spn_trylock(&packet_pool.lock)) {
129
+ defer(sock_packet_free_attempt, packet_, ignr);
130
+ return;
131
+ }
132
+ packet_s *packet = packet_;
133
+ packet->next = packet_pool.next;
134
+ packet_pool.next = packet;
135
+ spn_unlock(&packet_pool.lock);
121
136
  }
122
137
 
123
138
  static inline void sock_packet_free(packet_s *packet) {
124
- sock_packet_clear(packet);
139
+ if (packet->free_func == fio_free) {
140
+ fio_free(packet->buffer);
141
+ } else if (packet->free_func == free) {
142
+ free(packet->buffer);
143
+ } else {
144
+ defer(sock_packet_free_cb, (void *)((uintptr_t)packet->free_func),
145
+ packet->buffer);
146
+ }
125
147
  if (packet >= packet_pool.mem &&
126
148
  packet <= packet_pool.mem + (BUFFER_PACKET_POOL - 1)) {
127
- spn_lock(&packet_pool.lock);
128
- packet->metadata.next = packet_pool.next;
129
- packet_pool.next = packet;
130
- spn_unlock(&packet_pool.lock);
149
+ sock_packet_free_attempt(packet, NULL);
131
150
  } else
132
- free(packet);
151
+ fio_free(packet);
133
152
  }
134
153
 
135
- static inline packet_s *sock_packet_try_grab(void) {
154
+ static inline packet_s *sock_packet_new(void) {
136
155
  packet_s *packet;
137
- spn_lock(&packet_pool.lock);
156
+ if (spn_trylock(&packet_pool.lock))
157
+ goto no_lock;
138
158
  packet = packet_pool.next;
139
159
  if (packet == NULL)
140
160
  goto none_in_pool;
141
- packet_pool.next = packet->metadata.next;
161
+ packet_pool.next = packet->next;
142
162
  spn_unlock(&packet_pool.lock);
143
- packet->metadata = (struct packet_metadata_s){
144
- .free_func = (void (*)(packet_s *))SOCK_DEALLOC_NOOP};
145
- packet->buffer.len = 0;
146
163
  return packet;
147
164
  none_in_pool:
148
165
  if (!packet_pool.init)
149
166
  goto init;
150
167
  spn_unlock(&packet_pool.lock);
151
- return NULL;
168
+ no_lock:
169
+ packet = fio_malloc(sizeof(*packet));
170
+ if (!packet) {
171
+ perror("FATAL ERROR: memory allocation failed");
172
+ exit(errno);
173
+ }
174
+ return packet;
152
175
  init:
153
176
  packet_pool.init = 1;
154
- packet_pool.mem[0].metadata.free_func =
155
- (void (*)(packet_s *))SOCK_DEALLOC_NOOP;
156
177
  for (size_t i = 2; i < BUFFER_PACKET_POOL; i++) {
157
- packet_pool.mem[i - 1].metadata.next = packet_pool.mem + i;
158
- packet_pool.mem[i - 1].metadata.free_func =
159
- (void (*)(packet_s *))SOCK_DEALLOC_NOOP;
178
+ packet_pool.mem[i - 1].next = packet_pool.mem + i;
160
179
  }
161
- packet_pool.mem[BUFFER_PACKET_POOL - 1].metadata.free_func =
162
- (void (*)(packet_s *))SOCK_DEALLOC_NOOP;
163
180
  packet_pool.next = packet_pool.mem + 1;
164
181
  spn_unlock(&packet_pool.lock);
165
182
  packet = packet_pool.mem;
166
- packet->metadata = (struct packet_metadata_s){
167
- .free_func = (void (*)(packet_s *))SOCK_DEALLOC_NOOP};
168
- packet->buffer.len = 0;
169
- return packet;
170
- }
171
-
172
- static inline packet_s *sock_packet_grab(void) {
173
- packet_s *packet = sock_packet_try_grab();
174
- if (packet)
175
- return packet;
176
- while (packet == NULL) {
177
- sock_flush_all();
178
- packet = sock_packet_try_grab();
179
- };
180
183
  return packet;
181
184
  }
182
185
 
@@ -184,22 +187,29 @@ static inline packet_s *sock_packet_grab(void) {
184
187
  Default Socket Read/Write Hook
185
188
  ***************************************************************************** */
186
189
 
187
- static ssize_t sock_default_hooks_read(intptr_t uuid, void *buf, size_t count) {
190
+ static ssize_t sock_default_hooks_read(intptr_t uuid, void *udata, void *buf,
191
+ size_t count) {
188
192
  return read(sock_uuid2fd(uuid), buf, count);
193
+ (void)(udata);
189
194
  }
190
- static ssize_t sock_default_hooks_write(intptr_t uuid, const void *buf,
191
- size_t count) {
195
+ static ssize_t sock_default_hooks_write(intptr_t uuid, void *udata,
196
+ const void *buf, size_t count) {
192
197
  return write(sock_uuid2fd(uuid), buf, count);
198
+ (void)(udata);
193
199
  }
194
200
 
195
201
  static void sock_default_hooks_on_close(intptr_t fduuid,
196
- struct sock_rw_hook_s *rw_hook) {
202
+ struct sock_rw_hook_s *rw_hook,
203
+ void *udata) {
204
+ (void)udata;
197
205
  (void)rw_hook;
198
206
  (void)fduuid;
199
207
  }
200
208
 
201
- static ssize_t sock_default_hooks_flush(intptr_t uuid) {
202
- return (((void)(uuid)), 0);
209
+ static ssize_t sock_default_hooks_flush(intptr_t uuid, void *udata) {
210
+ return 0;
211
+ (void)(uuid);
212
+ (void)(udata);
203
213
  }
204
214
 
205
215
  const sock_rw_hook_s SOCK_DEFAULT_HOOKS = {
@@ -208,6 +218,7 @@ const sock_rw_hook_s SOCK_DEFAULT_HOOKS = {
208
218
  .flush = sock_default_hooks_flush,
209
219
  .on_close = sock_default_hooks_on_close,
210
220
  };
221
+
211
222
  /* *****************************************************************************
212
223
  Socket Data Structures
213
224
  ***************************************************************************** */
@@ -222,32 +233,37 @@ struct fd_data_s {
222
233
  unsigned close : 1;
223
234
  /** future flags. */
224
235
  unsigned rsv : 5;
225
- /** data sent from current packet - this is per packet. */
226
- size_t sent;
227
236
  /** the currently active packet to be sent. */
228
237
  packet_s *packet;
238
+ /** the last packet in the queue. */
239
+ packet_s **packet_last;
240
+ /** The number of pending packets that are in the queue. */
241
+ size_t packet_count;
229
242
  /** RW hooks. */
230
243
  sock_rw_hook_s *rw_hooks;
244
+ /** RW udata. */
245
+ void *rw_udata;
231
246
  /** Peer/listenning address. */
232
247
  struct sockaddr_in6 addrinfo;
233
248
  /** address length. */
234
249
  socklen_t addrlen;
235
250
  };
236
251
 
237
- static struct sock_data_store {
252
+ static struct sock_data_store_s {
238
253
  size_t capacity;
239
254
  struct fd_data_s *fds;
240
255
  } sock_data_store;
241
256
 
242
257
  #define fd2uuid(fd) \
243
- (((uintptr_t)(fd) << 8) | (sock_data_store.fds[(fd)].counter))
258
+ (((uintptr_t)(fd) << 8) | (sock_data_store.fds[(fd)].counter & 0xFF))
244
259
  #define fdinfo(fd) sock_data_store.fds[(fd)]
260
+ #define uuidinfo(fd) sock_data_store.fds[sock_uuid2fd((fd))]
245
261
 
246
262
  #define lock_fd(fd) spn_lock(&sock_data_store.fds[(fd)].lock)
247
263
  #define unlock_fd(fd) spn_unlock(&sock_data_store.fds[(fd)].lock)
248
264
 
249
265
  static inline int validate_uuid(uintptr_t uuid) {
250
- uintptr_t fd = sock_uuid2fd(uuid);
266
+ uintptr_t fd = (uintptr_t)sock_uuid2fd(uuid);
251
267
  if ((intptr_t)uuid == -1 || sock_data_store.capacity <= fd ||
252
268
  fdinfo(fd).counter != (uuid & 0xFF))
253
269
  return -1;
@@ -256,28 +272,39 @@ static inline int validate_uuid(uintptr_t uuid) {
256
272
 
257
273
  static inline void sock_packet_rotate_unsafe(uintptr_t fd) {
258
274
  packet_s *packet = fdinfo(fd).packet;
259
- fdinfo(fd).packet = packet->metadata.next;
260
- fdinfo(fd).sent = 0;
275
+ fdinfo(fd).packet = packet->next;
276
+ if (&packet->next == fdinfo(fd).packet_last) {
277
+ fdinfo(fd).packet_last = &fdinfo(fd).packet;
278
+ }
279
+ --fdinfo(fd).packet_count;
261
280
  sock_packet_free(packet);
262
281
  }
263
282
 
264
- static void clear_sock_lib(void) { free(sock_data_store.fds); }
283
+ static void clear_sock_lib(void) {
284
+ free(sock_data_store.fds);
285
+ sock_data_store.fds = NULL;
286
+ sock_data_store.capacity = 0;
287
+ }
265
288
 
266
289
  static inline int initialize_sock_lib(size_t capacity) {
267
290
  static uint8_t init_exit = 0;
291
+ if (capacity > LIB_SOCK_MAX_CAPACITY)
292
+ capacity = LIB_SOCK_MAX_CAPACITY;
268
293
  if (sock_data_store.capacity >= capacity)
269
- return 0;
294
+ goto finish;
270
295
  struct fd_data_s *new_collection =
271
- realloc(sock_data_store.fds, sizeof(struct fd_data_s) * capacity);
296
+ realloc(sock_data_store.fds, sizeof(*new_collection) * capacity);
272
297
  if (!new_collection)
273
298
  return -1;
274
299
  sock_data_store.fds = new_collection;
275
300
  for (size_t i = sock_data_store.capacity; i < capacity; i++) {
276
- fdinfo(i) =
277
- (struct fd_data_s){.open = 0,
278
- .lock = SPN_LOCK_INIT,
279
- .rw_hooks = (sock_rw_hook_s *)&SOCK_DEFAULT_HOOKS,
280
- .counter = 0};
301
+ fdinfo(i) = (struct fd_data_s){
302
+ .open = 0,
303
+ .lock = SPN_LOCK_INIT,
304
+ .rw_hooks = (sock_rw_hook_s *)&SOCK_DEFAULT_HOOKS,
305
+ .packet_last = &fdinfo(i).packet,
306
+ .counter = 0,
307
+ };
281
308
  }
282
309
  sock_data_store.capacity = capacity;
283
310
 
@@ -286,7 +313,7 @@ static inline int initialize_sock_lib(size_t capacity) {
286
313
  "\nInitialized libsock for %lu sockets, "
287
314
  "each one requires %lu bytes.\n"
288
315
  "overall ovearhead: %lu bytes.\n"
289
- "Initialized packet pool for %d elements, "
316
+ "Initialized packet pool for %lu elements, "
290
317
  "each one %lu bytes.\n"
291
318
  "overall buffer ovearhead: %lu bytes.\n"
292
319
  "=== Socket Library Total: %lu bytes ===\n\n",
@@ -297,6 +324,11 @@ static inline int initialize_sock_lib(size_t capacity) {
297
324
  (sizeof(struct fd_data_s) * capacity));
298
325
  #endif
299
326
 
327
+ finish:
328
+ packet_pool.lock = SPN_LOCK_INIT;
329
+ for (size_t i = 0; i < sock_data_store.capacity; ++i) {
330
+ sock_data_store.fds[i].lock = SPN_LOCK_INIT;
331
+ }
300
332
  if (init_exit)
301
333
  return 0;
302
334
  init_exit = 1;
@@ -311,26 +343,30 @@ static inline int clear_fd(uintptr_t fd, uint8_t is_open) {
311
343
  clear:
312
344
  spn_lock(&(fdinfo(fd).lock));
313
345
  struct fd_data_s old_data = fdinfo(fd);
314
- sock_data_store.fds[fd] =
315
- (struct fd_data_s){.open = is_open,
316
- .lock = fdinfo(fd).lock,
317
- .rw_hooks = (sock_rw_hook_s *)&SOCK_DEFAULT_HOOKS,
318
- .counter = fdinfo(fd).counter + 1};
346
+ sock_data_store.fds[fd] = (struct fd_data_s){
347
+ .open = is_open,
348
+ .lock = fdinfo(fd).lock,
349
+ .rw_hooks = (sock_rw_hook_s *)&SOCK_DEFAULT_HOOKS,
350
+ .counter = fdinfo(fd).counter + 1,
351
+ .packet_last = &sock_data_store.fds[fd].packet,
352
+ };
319
353
  spn_unlock(&(fdinfo(fd).lock));
320
- packet = old_data.packet;
321
354
  while (old_data.packet) {
322
- old_data.packet = old_data.packet->metadata.next;
323
- sock_packet_free(packet);
324
355
  packet = old_data.packet;
356
+ old_data.packet = old_data.packet->next;
357
+ sock_packet_free(packet);
325
358
  }
326
- old_data.rw_hooks->on_close(((fd << 8) | old_data.counter),
327
- old_data.rw_hooks);
328
- if (old_data.open || (old_data.rw_hooks != &SOCK_DEFAULT_HOOKS)) {
359
+ old_data.rw_hooks->on_close(((fd << 8) | old_data.counter), old_data.rw_hooks,
360
+ old_data.rw_udata);
361
+ if (old_data.open) {
329
362
  sock_on_close((fd << 8) | old_data.counter);
330
- evio_remove((fd << 8) | old_data.counter);
331
363
  }
332
364
  return 0;
333
365
  reinitialize:
366
+ if (fd >= LIB_SOCK_MAX_CAPACITY) {
367
+ close(fd);
368
+ return -1;
369
+ }
334
370
  if (initialize_sock_lib(fd << 1))
335
371
  return -1;
336
372
  goto clear;
@@ -340,96 +376,64 @@ reinitialize:
340
376
  Writing - from memory
341
377
  ***************************************************************************** */
342
378
 
343
- struct sock_packet_ext_data_s {
344
- uint8_t *buffer;
345
- uint8_t *to_free;
346
- void (*dealloc)(void *);
347
- };
348
-
349
379
  static int sock_write_buffer(int fd, struct packet_s *packet) {
350
380
  int written = fdinfo(fd).rw_hooks->write(
351
- fd2uuid(fd), packet->buffer.buf + fdinfo(fd).sent,
352
- packet->buffer.len - fdinfo(fd).sent);
381
+ fd2uuid(fd), fdinfo(fd).rw_udata,
382
+ ((uint8_t *)packet->buffer + packet->offset), packet->length);
353
383
  if (written > 0) {
354
- fdinfo(fd).sent += written;
355
- if (fdinfo(fd).sent == packet->buffer.len)
384
+ packet->length -= written;
385
+ packet->offset += written;
386
+ if (!packet->length)
356
387
  sock_packet_rotate_unsafe(fd);
357
388
  }
358
389
  return written;
359
390
  }
360
391
 
361
- static int sock_write_buffer_ext(int fd, struct packet_s *packet) {
362
- struct sock_packet_ext_data_s *ext = (void *)packet->buffer.buf;
363
- int written =
364
- fdinfo(fd).rw_hooks->write(fd2uuid(fd), ext->buffer + fdinfo(fd).sent,
365
- packet->buffer.len - fdinfo(fd).sent);
366
- if (written > 0) {
367
- fdinfo(fd).sent += written;
368
- if (fdinfo(fd).sent == packet->buffer.len)
369
- sock_packet_rotate_unsafe(fd);
370
- }
371
- return written;
372
- }
373
-
374
- static void sock_free_buffer_ext(packet_s *packet) {
375
- struct sock_packet_ext_data_s *ext = (void *)packet->buffer.buf;
376
- ext->dealloc(ext->to_free);
377
- }
378
-
379
392
  /* *****************************************************************************
380
393
  Writing - from files
381
394
  ***************************************************************************** */
382
395
 
383
- struct sock_packet_file_data_s {
384
- intptr_t fd;
385
- off_t offset;
386
- union {
387
- void (*close)(intptr_t);
388
- void (*dealloc)(void *);
389
- };
390
- int *pfd;
391
- uint8_t buffer[];
392
- };
396
+ #ifndef BUFFER_FILE_READ_SIZE
397
+ #define BUFFER_FILE_READ_SIZE 16384
398
+ #endif
393
399
 
394
400
  static void sock_perform_close_fd(intptr_t fd) { close(fd); }
395
- static void sock_perform_close_pfd(void *pfd) { close(*(int *)pfd); }
396
-
397
- static void sock_close_from_fd(packet_s *packet) {
398
- struct sock_packet_file_data_s *ext = (void *)packet->buffer.buf;
399
- if (ext->pfd)
400
- ext->dealloc(ext->pfd);
401
- else
402
- ext->close(ext->fd);
401
+ static void sock_perform_close_pfd(void *pfd) {
402
+ close(*(int *)pfd);
403
+ free(pfd);
403
404
  }
404
405
 
405
406
  static int sock_write_from_fd(int fd, struct packet_s *packet) {
406
- struct sock_packet_file_data_s *ext = (void *)packet->buffer.buf;
407
- ssize_t count = 0;
407
+ ssize_t asked = 0;
408
+ ssize_t sent = 0;
409
+ ssize_t total = 0;
410
+ char buff[BUFFER_FILE_READ_SIZE];
408
411
  do {
409
- fdinfo(fd).sent += count;
410
- packet->buffer.len -= count;
412
+ packet->offset += sent;
413
+ packet->length -= sent;
411
414
  retry:
412
- count = (packet->buffer.len < BUFFER_FILE_READ_SIZE)
413
- ? pread(ext->fd, ext->buffer, packet->buffer.len,
414
- ext->offset + fdinfo(fd).sent)
415
- : pread(ext->fd, ext->buffer, BUFFER_FILE_READ_SIZE,
416
- ext->offset + fdinfo(fd).sent);
417
- if (count <= 0)
415
+ asked =
416
+ (packet->length < BUFFER_FILE_READ_SIZE)
417
+ ? pread(packet->fd, buff, packet->length, packet->offset)
418
+ : pread(packet->fd, buff, BUFFER_FILE_READ_SIZE, packet->offset);
419
+ if (asked <= 0)
418
420
  goto read_error;
419
- count = fdinfo(fd).rw_hooks->write(fd2uuid(fd), ext->buffer, count);
420
- } while (count == BUFFER_FILE_READ_SIZE && packet->buffer.len);
421
- if (count >= 0) {
422
- fdinfo(fd).sent += count;
423
- packet->buffer.len -= count;
424
- if (!packet->buffer.len) {
421
+ sent = fdinfo(fd).rw_hooks->write(fd2uuid(fd), fdinfo(fd).rw_udata, buff,
422
+ asked);
423
+ } while (sent == asked && packet->length);
424
+ if (sent >= 0) {
425
+ packet->offset += sent;
426
+ packet->length -= sent;
427
+ total += sent;
428
+ if (!packet->length) {
425
429
  sock_packet_rotate_unsafe(fd);
426
430
  return 1;
427
431
  }
428
432
  }
429
- return count;
433
+ return total;
430
434
 
431
435
  read_error:
432
- if (count == 0) {
436
+ if (sent == 0) {
433
437
  sock_packet_rotate_unsafe(fd);
434
438
  return 1;
435
439
  }
@@ -438,48 +442,44 @@ read_error:
438
442
  return -1;
439
443
  }
440
444
 
441
- #if USE_SENDFILE == 1
442
-
443
- #if defined(__linux__) /* linux sendfile API */
445
+ #if USE_SENDFILE && defined(__linux__) /* linux sendfile API */
444
446
 
445
447
  static int sock_sendfile_from_fd(int fd, struct packet_s *packet) {
446
- struct sock_packet_file_data_s *ext = (void *)packet->buffer.buf;
447
448
  ssize_t sent;
448
- sent = sendfile64(fd, ext->fd, &ext->offset, packet->buffer.len);
449
+ sent = sendfile64(fd, packet->fd, &packet->offset, packet->length);
449
450
  if (sent < 0)
450
451
  return -1;
451
- packet->buffer.len -= sent;
452
- if (!packet->buffer.len)
452
+ packet->length -= sent;
453
+ if (!packet->length)
453
454
  sock_packet_rotate_unsafe(fd);
454
455
  return sent;
455
456
  }
456
457
 
457
- #elif defined(__APPLE__) || defined(__unix__) /* BSD / Apple API */
458
+ #elif USE_SENDFILE && \
459
+ (defined(__APPLE__) || defined(__unix__)) /* BSD / Apple API */
458
460
 
459
461
  static int sock_sendfile_from_fd(int fd, struct packet_s *packet) {
460
- struct sock_packet_file_data_s *ext = (void *)packet->buffer.buf;
461
462
  off_t act_sent = 0;
462
463
  ssize_t ret = 0;
463
- while (packet->buffer.len) {
464
- act_sent = packet->buffer.len;
464
+ while (packet->length) {
465
+ act_sent = packet->length;
465
466
  #if defined(__APPLE__)
466
- ret = sendfile(ext->fd, fd, ext->offset + fdinfo(fd).sent, &act_sent, NULL,
467
- 0);
467
+ ret = sendfile(packet->fd, fd, packet->offset, &act_sent, NULL, 0);
468
468
  #else
469
- ret = sendfile(ext->fd, fd, ext->offset + fdinfo(fd).sent, (size_t)act_sent,
470
- NULL, &act_sent, 0);
469
+ ret = sendfile(packet->fd, fd, packet->offset, (size_t)act_sent, NULL,
470
+ &act_sent, 0);
471
471
  #endif
472
472
  if (ret < 0)
473
473
  goto error;
474
- fdinfo(fd).sent += act_sent;
475
- packet->buffer.len -= act_sent;
474
+ packet->length -= act_sent;
475
+ packet->offset += act_sent;
476
476
  }
477
477
  sock_packet_rotate_unsafe(fd);
478
478
  return act_sent;
479
479
  error:
480
- if (errno == EAGAIN) {
481
- fdinfo(fd).sent += act_sent;
482
- packet->buffer.len -= act_sent;
480
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
481
+ packet->length -= act_sent;
482
+ packet->offset += act_sent;
483
483
  }
484
484
  return -1;
485
485
  }
@@ -515,19 +515,38 @@ static int (*sock_sendfile_from_fd)(int fd, struct packet_s *packet) =
515
515
  sock_write_from_fd;
516
516
 
517
517
  #endif
518
- #else
519
- static int (*sock_sendfile_from_fd)(int fd, struct packet_s *packet) =
520
- sock_write_from_fd;
521
- #endif
518
+
519
+ static int sock_sendfile_from_pfd(int fd, struct packet_s *packet) {
520
+ int ret;
521
+ struct packet_s tmp = *packet;
522
+ tmp.fd = ((intptr_t *)tmp.buffer)[0];
523
+ ret = sock_sendfile_from_fd(fd, &tmp);
524
+ tmp.fd = packet->fd;
525
+ *packet = tmp;
526
+ return ret;
527
+ }
528
+
529
+ static int sock_write_from_pfd(int fd, struct packet_s *packet) {
530
+ int ret;
531
+ struct packet_s tmp = *packet;
532
+ tmp.fd = ((intptr_t *)tmp.buffer)[0];
533
+ ret = sock_write_from_fd(fd, &tmp);
534
+ tmp.fd = packet->fd;
535
+ *packet = tmp;
536
+ return ret;
537
+ }
522
538
 
523
539
  /* *****************************************************************************
524
540
  The API
525
541
  ***************************************************************************** */
526
542
 
527
543
  /* *****************************************************************************
528
- Process wide and helper sock_API.
544
+ Process wide and helper sock API.
529
545
  */
530
546
 
547
+ /** MUST be called after forking a process. */
548
+ void sock_on_fork(void) { initialize_sock_lib(0); }
549
+
531
550
  /**
532
551
  Sets a socket to non blocking state.
533
552
 
@@ -543,10 +562,12 @@ int sock_set_non_block(int fd) {
543
562
  flags = 0;
544
563
  // printf("flags initial value was %d\n", flags);
545
564
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
546
- #else
565
+ #elif defined(FIONBIO)
547
566
  /* Otherwise, use the old way of doing it */
548
567
  static int flags = 1;
549
- return ioctl(fd, FIOBIO, &flags);
568
+ return ioctl(fd, FIONBIO, &flags);
569
+ #else
570
+ #error No functions / argumnet macros for non-blocking sockets.
550
571
  #endif
551
572
  }
552
573
 
@@ -564,35 +585,42 @@ ssize_t sock_max_capacity(void) {
564
585
  return flim;
565
586
  #ifdef _SC_OPEN_MAX
566
587
  flim = sysconf(_SC_OPEN_MAX);
567
- #elif defined(OPEN_MAX)
568
- flim = OPEN_MAX;
588
+ #elif defined(FOPEN_MAX)
589
+ flim = FOPEN_MAX;
569
590
  #endif
570
591
  // try to maximize limits - collect max and set to max
571
592
  struct rlimit rlim = {.rlim_max = 0};
572
- getrlimit(RLIMIT_NOFILE, &rlim);
573
- // printf("Meximum open files are %llu out of %llu\n", rlim.rlim_cur,
574
- // rlim.rlim_max);
575
- #if defined(__APPLE__) /* Apple's getrlimit is broken. */
576
- rlim.rlim_cur = rlim.rlim_max >= OPEN_MAX ? OPEN_MAX : rlim.rlim_max;
577
- #else
578
- rlim.rlim_cur = rlim.rlim_max;
579
- #endif
580
-
581
- setrlimit(RLIMIT_NOFILE, &rlim);
582
- getrlimit(RLIMIT_NOFILE, &rlim);
583
- // printf("Meximum open files are %llu out of %llu\n", rlim.rlim_cur,
584
- // rlim.rlim_max);
585
- // if the current limit is higher than it was, update
586
- if (flim < ((ssize_t)rlim.rlim_cur))
593
+ if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
594
+ fprintf(stderr, "WARNING: `getrlimit` failed in `sock_max_capacity`.\n");
595
+ } else {
596
+ // #if defined(__APPLE__) /* Apple's getrlimit is broken. */
597
+ // rlim.rlim_cur = rlim.rlim_max >= FOPEN_MAX ? FOPEN_MAX :
598
+ // rlim.rlim_max;
599
+ // #else
600
+ rlim.rlim_cur = rlim.rlim_max;
601
+ // #endif
602
+
603
+ if (rlim.rlim_cur > LIB_SOCK_MAX_CAPACITY)
604
+ rlim.rlim_cur = LIB_SOCK_MAX_CAPACITY;
605
+
606
+ if (!setrlimit(RLIMIT_NOFILE, &rlim))
607
+ getrlimit(RLIMIT_NOFILE, &rlim);
587
608
  flim = rlim.rlim_cur;
609
+ }
610
+ #if DEBUG
611
+ fprintf(stderr,
612
+ "libsock capacity initialization:\n"
613
+ "* Meximum open files %lu out of %lu\n",
614
+ (unsigned long)flim, (unsigned long)rlim.rlim_max);
615
+ #endif
588
616
  // initialize library to maximum capacity
589
617
  initialize_sock_lib(flim);
590
618
  // return what we have
591
- return flim;
619
+ return sock_data_store.capacity;
592
620
  }
593
621
 
594
622
  /* *****************************************************************************
595
- The main sock_API.
623
+ The main sock API.
596
624
  */
597
625
 
598
626
  /**
@@ -611,60 +639,110 @@ the same `fd`).
611
639
  */
612
640
  intptr_t sock_listen(const char *address, const char *port) {
613
641
  int srvfd;
614
- // setup the address
615
- struct addrinfo hints;
616
- struct addrinfo *servinfo; // will point to the results
617
- memset(&hints, 0, sizeof hints); // make sure the struct is empty
618
- hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
619
- hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
620
- hints.ai_flags = AI_PASSIVE; // fill in my IP for me
621
- if (getaddrinfo(address, port, &hints, &servinfo)) {
622
- // perror("addr err");
623
- return -1;
624
- }
625
- // get the file descriptor
626
- srvfd =
627
- socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
628
- if (srvfd <= 0) {
629
- // perror("socket err");
630
- freeaddrinfo(servinfo);
631
- return -1;
632
- }
633
- // make sure the socket is non-blocking
634
- if (sock_set_non_block(srvfd) < 0) {
635
- // perror("couldn't set socket as non blocking! ");
636
- freeaddrinfo(servinfo);
637
- close(srvfd);
638
- return -1;
639
- }
640
- // avoid the "address taken"
641
- {
642
- int optval = 1;
643
- setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
644
- }
645
- // bind the address to the socket
646
- {
647
- int bound = 0;
648
- for (struct addrinfo *p = servinfo; p != NULL; p = p->ai_next) {
649
- if (!bind(srvfd, p->ai_addr, p->ai_addrlen))
650
- bound = 1;
642
+ if (!port || *port == 0 || (port[0] == '0' && port[1] == 0)) {
643
+ /* Unix socket */
644
+ if (!address) {
645
+ errno = EINVAL;
646
+ fprintf(
647
+ stderr,
648
+ "ERROR: (sock) sock_listen - a Unix socket requires a valid address."
649
+ " or specify port for TCP/IP.\n");
650
+ return -1;
651
+ }
652
+ struct sockaddr_un addr = {0};
653
+ size_t addr_len = strlen(address);
654
+ if (addr_len >= sizeof(addr.sun_path)) {
655
+ errno = ENAMETOOLONG;
656
+ return -1;
657
+ }
658
+ addr.sun_family = AF_UNIX;
659
+ memcpy(addr.sun_path, address, addr_len + 1); /* copy the NUL byte. */
660
+ #if defined(__APPLE__)
661
+ addr.sun_len = addr_len;
662
+ #endif
663
+ // get the file descriptor
664
+ srvfd = socket(AF_UNIX, SOCK_STREAM, 0);
665
+ if (srvfd == -1) {
666
+ return -1;
651
667
  }
668
+ if (sock_set_non_block(srvfd) == -1) {
669
+ close(srvfd);
670
+ return -1;
671
+ }
672
+ unlink(addr.sun_path);
673
+ if (bind(srvfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
674
+ close(srvfd);
675
+ return -1;
676
+ }
677
+ /* chmod for foriegn connections */
678
+ fchmod(srvfd, 0777);
652
679
 
653
- if (!bound) {
654
- // perror("bind err");
680
+ } else {
681
+ /* TCP/IP socket */
682
+ // setup the address
683
+ struct addrinfo hints = {0};
684
+ struct addrinfo *servinfo; // will point to the results
685
+ memset(&hints, 0, sizeof hints); // make sure the struct is empty
686
+ hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
687
+ hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
688
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
689
+ if (getaddrinfo(address, port, &hints, &servinfo)) {
690
+ // perror("addr err");
691
+ return -1;
692
+ }
693
+ // get the file descriptor
694
+ srvfd = socket(servinfo->ai_family, servinfo->ai_socktype,
695
+ servinfo->ai_protocol);
696
+ if (srvfd <= 0) {
697
+ // perror("socket err");
698
+ freeaddrinfo(servinfo);
699
+ return -1;
700
+ }
701
+ // make sure the socket is non-blocking
702
+ if (sock_set_non_block(srvfd) < 0) {
703
+ // perror("couldn't set socket as non blocking! ");
655
704
  freeaddrinfo(servinfo);
656
705
  close(srvfd);
657
706
  return -1;
658
707
  }
708
+ // avoid the "address taken"
709
+ {
710
+ int optval = 1;
711
+ setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
712
+ }
713
+ // bind the address to the socket
714
+ {
715
+ int bound = 0;
716
+ for (struct addrinfo *p = servinfo; p != NULL; p = p->ai_next) {
717
+ if (!bind(srvfd, p->ai_addr, p->ai_addrlen))
718
+ bound = 1;
719
+ }
720
+
721
+ if (!bound) {
722
+ // perror("bind err");
723
+ freeaddrinfo(servinfo);
724
+ close(srvfd);
725
+ return -1;
726
+ }
727
+ }
728
+ #ifdef TCP_FASTOPEN
729
+ // support TCP Fast Open when available
730
+ {
731
+ int optval = 128;
732
+ setsockopt(srvfd, servinfo->ai_protocol, TCP_FASTOPEN, &optval,
733
+ sizeof(optval));
734
+ }
735
+ #endif
736
+ freeaddrinfo(servinfo);
659
737
  }
660
- freeaddrinfo(servinfo);
661
738
  // listen in
662
739
  if (listen(srvfd, SOMAXCONN) < 0) {
663
740
  // perror("couldn't start listening");
664
741
  close(srvfd);
665
742
  return -1;
666
743
  }
667
- clear_fd(srvfd, 1);
744
+ if (clear_fd(srvfd, 1))
745
+ return -1;
668
746
  return fd2uuid(srvfd);
669
747
  }
670
748
 
@@ -688,7 +766,6 @@ intptr_t sock_accept(intptr_t srv_uuid) {
688
766
  struct sockaddr_in6 addrinfo;
689
767
  socklen_t addrlen = sizeof(addrinfo);
690
768
  int client;
691
- int one = 1;
692
769
  #ifdef SOCK_NONBLOCK
693
770
  client = accept4(sock_uuid2fd(srv_uuid), (struct sockaddr *)&addrinfo,
694
771
  &addrlen, SOCK_NONBLOCK);
@@ -701,8 +778,25 @@ intptr_t sock_accept(intptr_t srv_uuid) {
701
778
  return -1;
702
779
  sock_set_non_block(client);
703
780
  #endif
704
- setsockopt(client, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
705
- clear_fd(client, 1);
781
+ // avoid the TCP delay algorithm.
782
+ {
783
+ int optval = 1;
784
+ setsockopt(client, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
785
+ }
786
+ // handle socket buffers.
787
+ {
788
+ int optval = 0;
789
+ socklen_t size = (socklen_t)sizeof(optval);
790
+ if (!getsockopt(client, SOL_SOCKET, SO_SNDBUF, &optval, &size) &&
791
+ optval <= 131072) {
792
+ optval = 131072;
793
+ setsockopt(client, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval));
794
+ optval = 131072;
795
+ setsockopt(client, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval));
796
+ }
797
+ }
798
+ if (clear_fd(client, 1))
799
+ return -1;
706
800
  fdinfo(client).addrinfo = addrinfo;
707
801
  fdinfo(client).addrlen = addrlen;
708
802
  return fd2uuid(client);
@@ -735,47 +829,92 @@ before attempting to write to the socket.
735
829
  intptr_t sock_connect(char *address, char *port) {
736
830
  int fd;
737
831
  int one = 1;
738
- // setup the address
739
- struct addrinfo hints;
740
- struct addrinfo *addrinfo; // will point to the results
741
- memset(&hints, 0, sizeof hints); // make sure the struct is empty
742
- hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
743
- hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
744
- hints.ai_flags = AI_PASSIVE; // fill in my IP for me
745
- if (getaddrinfo(address, port, &hints, &addrinfo)) {
746
- return -1;
747
- }
748
- // get the file descriptor
749
- fd =
750
- socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
751
- if (fd <= 0) {
752
- freeaddrinfo(addrinfo);
753
- return -1;
754
- }
755
- // make sure the socket is non-blocking
756
- if (sock_set_non_block(fd) < 0) {
832
+ if (!port || *port == 0 || (port[0] == '0' && port[1] == 0)) {
833
+ /* Unix socket */
834
+ if (!address) {
835
+ errno = EINVAL;
836
+ fprintf(
837
+ stderr,
838
+ "ERROR: (sock) sock_listen - a Unix socket requires a valid address."
839
+ " or specify port for TCP/IP.\n");
840
+ return -1;
841
+ }
842
+
843
+ struct sockaddr_un addr = {.sun_family = AF_UNIX};
844
+ size_t addr_len = strlen(address);
845
+ if (addr_len >= sizeof(addr.sun_path)) {
846
+ errno = ENAMETOOLONG;
847
+ return -1;
848
+ }
849
+ addr.sun_family = AF_UNIX;
850
+ memcpy(addr.sun_path, address, addr_len + 1); /* copy the NUL byte. */
851
+ #if defined(__APPLE__)
852
+ addr.sun_len = addr_len;
853
+ #endif
854
+ // get the file descriptor
855
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
856
+ if (fd == -1) {
857
+ return -1;
858
+ }
859
+ if (sock_set_non_block(fd) == -1) {
860
+ close(fd);
861
+ return -1;
862
+ }
863
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
864
+ errno != EINPROGRESS) {
865
+ close(fd);
866
+ return -1;
867
+ }
868
+ if (clear_fd(fd, 1))
869
+ return -1;
870
+ } else {
871
+ // setup the address
872
+ struct addrinfo hints;
873
+ struct addrinfo *addrinfo; // will point to the results
874
+ memset(&hints, 0, sizeof hints); // make sure the struct is empty
875
+ hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
876
+ hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
877
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
878
+ if (getaddrinfo(address, port, &hints, &addrinfo)) {
879
+ return -1;
880
+ }
881
+ // get the file descriptor
882
+ fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
883
+ addrinfo->ai_protocol);
884
+ if (fd <= 0) {
885
+ freeaddrinfo(addrinfo);
886
+ return -1;
887
+ }
888
+ // make sure the socket is non-blocking
889
+ if (sock_set_non_block(fd) < 0) {
890
+ freeaddrinfo(addrinfo);
891
+ close(fd);
892
+ return -1;
893
+ }
894
+
895
+ for (struct addrinfo *i = addrinfo; i; i = i->ai_next) {
896
+ if (connect(fd, i->ai_addr, i->ai_addrlen) == 0 || errno == EINPROGRESS)
897
+ goto connection_requested;
898
+ }
757
899
  freeaddrinfo(addrinfo);
758
900
  close(fd);
759
901
  return -1;
760
- }
761
902
 
762
- if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) < 0 &&
763
- errno != EINPROGRESS) {
764
- close(fd);
903
+ connection_requested:
904
+
905
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
906
+ if (clear_fd(fd, 1))
907
+ return -1;
908
+ memcpy(&fdinfo(fd).addrinfo, addrinfo->ai_addr, addrinfo->ai_addrlen);
909
+ fdinfo(fd).addrlen = addrinfo->ai_addrlen;
765
910
  freeaddrinfo(addrinfo);
766
- return -1;
767
911
  }
768
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
769
- clear_fd(fd, 1);
770
- fdinfo(fd).addrinfo = *((struct sockaddr_in6 *)addrinfo->ai_addr);
771
- fdinfo(fd).addrlen = addrinfo->ai_addrlen;
772
- freeaddrinfo(addrinfo);
773
912
  return fd2uuid(fd);
774
913
  }
775
914
 
776
915
  /**
777
916
  `sock_open` takes an existing file descriptor `fd` and initializes it's status
778
- as open and available for `sock_API` calls, returning a valid UUID.
917
+ as open and available for `sock_*` API calls, returning a valid UUID.
779
918
 
780
919
  This will reinitialize the data (user buffer etc') for the file descriptor
781
920
  provided, calling the `reactor_on_close` callback if the `fd` was previously
@@ -793,17 +932,36 @@ response in the background while a disconnection and a new connection occur on
793
932
  the same `fd`).
794
933
  */
795
934
  intptr_t sock_open(int fd) {
796
- clear_fd(fd, 1);
935
+ if (clear_fd(fd, 1))
936
+ return -1;
797
937
  return fd2uuid(fd);
798
938
  }
799
939
 
940
+ /**
941
+ * `sock_hijack` is the reverse of the `sock_open` function, removing the
942
+ * connection from the `sock` library and clearing it's data without closing it
943
+ * (`sock_on_close` will NOT be called).
944
+ *
945
+ * Returns the original `fd` for the socket. On error returns -1.
946
+ */
947
+ int sock_hijack(intptr_t uuid) {
948
+ const int fd = sock_uuid2fd(uuid);
949
+ if (validate_uuid(uuid) && fdinfo(fd).open) {
950
+ fprintf(stderr, "WARNING: SOCK HIJACK FAILING!\n");
951
+ return -1;
952
+ }
953
+ fdinfo(fd).open = 0;
954
+ clear_fd(fd, 0);
955
+ return fd;
956
+ }
957
+
800
958
  /** Returns the information available about the socket's peer address. */
801
959
  sock_peer_addr_s sock_peer_addr(intptr_t uuid) {
802
960
  if (validate_uuid(uuid) || !fdinfo(sock_uuid2fd(uuid)).addrlen)
803
961
  return (sock_peer_addr_s){.addr = NULL};
804
962
  return (sock_peer_addr_s){
805
- .addrlen = fdinfo(sock_uuid2fd(uuid)).addrlen,
806
- .addr = (struct sockaddr *)&fdinfo(sock_uuid2fd(uuid)).addrinfo,
963
+ .addrlen = uuidinfo(uuid).addrlen,
964
+ .addr = (struct sockaddr *)&uuidinfo(uuid).addrinfo,
807
965
  };
808
966
  }
809
967
 
@@ -813,7 +971,16 @@ Returns 1 if the uuid refers to a valid and open, socket.
813
971
  Returns 0 if not.
814
972
  */
815
973
  int sock_isvalid(intptr_t uuid) {
816
- return validate_uuid(uuid) == 0 && fdinfo(sock_uuid2fd(uuid)).open;
974
+ return validate_uuid(uuid) == 0 && uuidinfo(uuid).open;
975
+ }
976
+
977
+ /**
978
+ Returns 1 if the uuid is invalid or the socket is flagged to be closed.
979
+
980
+ Returns 0 if the socket is valid, open and isn't flagged to be closed.
981
+ */
982
+ int sock_isclosed(intptr_t uuid) {
983
+ return validate_uuid(uuid) || !uuidinfo(uuid).open || uuidinfo(uuid).close;
817
984
  }
818
985
 
819
986
  /**
@@ -860,17 +1027,22 @@ ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
860
1027
  return -1;
861
1028
  }
862
1029
  sock_rw_hook_s *rw = fdinfo(sock_uuid2fd(uuid)).rw_hooks;
1030
+ void *udata = fdinfo(sock_uuid2fd(uuid)).rw_udata;
863
1031
  unlock_fd(sock_uuid2fd(uuid));
864
1032
  if (count == 0)
865
- return rw->read(uuid, buf, count);
1033
+ return rw->read(uuid, udata, buf, count);
866
1034
  int old_errno = errno;
867
- ssize_t ret = rw->read(uuid, buf, count);
1035
+ ssize_t ret;
1036
+ retry_int:
1037
+ ret = rw->read(uuid, udata, buf, count);
868
1038
  if (ret > 0) {
869
1039
  sock_touch(uuid);
870
1040
  return ret;
871
1041
  }
872
- if (ret < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR ||
873
- errno == ENOTCONN)) {
1042
+ if (ret < 0 && errno == EINTR)
1043
+ goto retry_int;
1044
+ if (ret < 0 &&
1045
+ (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOTCONN)) {
874
1046
  errno = old_errno;
875
1047
  return 0;
876
1048
  }
@@ -884,88 +1056,63 @@ ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
884
1056
  ssize_t sock_write2_fn(sock_write_info_s options) {
885
1057
  int fd = sock_uuid2fd(options.uuid);
886
1058
 
887
- // avoid work when an error is expected to occur.
888
- if (validate_uuid(options.uuid) || !fdinfo(fd).open || options.offset < 0) {
889
- if (options.move == 0) {
890
- errno = (options.offset < 0) ? ERANGE : EBADF;
891
- return -1;
892
- }
893
- if (options.move)
894
- (options.dealloc ? options.dealloc : free)((void *)options.buffer);
895
- else
896
- (options.dealloc ? (void (*)(intptr_t))options.dealloc
897
- : sock_perform_close_fd)(options.data_fd);
898
- errno = (options.offset < 0) ? ERANGE : EBADF;
899
- return -1;
1059
+ /* this extra work can be avoided if an error is already known to occur...
1060
+ * but the extra complexity and branching isn't worth it, considering the
1061
+ * common case should be that there's no expected error.
1062
+ *
1063
+ * It also important to point out that errors should handle deallocation,
1064
+ * simplifying client-side error handling logic (this is a framework wide
1065
+ * design choice where callbacks are passed).
1066
+ */
1067
+ packet_s *packet = sock_packet_new();
1068
+ packet->length = options.length;
1069
+ packet->offset = options.offset;
1070
+ packet->buffer = (void *)options.buffer;
1071
+ if (options.is_fd) {
1072
+ packet->write_func = (fdinfo(fd).rw_hooks == &SOCK_DEFAULT_HOOKS)
1073
+ ? sock_sendfile_from_fd
1074
+ : sock_write_from_fd;
1075
+ packet->free_func =
1076
+ (options.dealloc ? options.dealloc
1077
+ : (void (*)(void *))sock_perform_close_fd);
1078
+ } else if (options.is_pfd) {
1079
+ packet->write_func = (fdinfo(fd).rw_hooks == &SOCK_DEFAULT_HOOKS)
1080
+ ? sock_sendfile_from_pfd
1081
+ : sock_write_from_pfd;
1082
+ packet->free_func =
1083
+ (options.dealloc ? options.dealloc : sock_perform_close_pfd);
1084
+ } else {
1085
+ packet->write_func = sock_write_buffer;
1086
+ packet->free_func = (options.dealloc ? options.dealloc : free);
900
1087
  }
901
1088
 
902
- packet_s *packet = sock_packet_grab();
903
- packet->buffer.len = options.length;
904
- if (options.is_fd == 0 && options.is_pfd == 0) { /* is data */
905
- if (options.move == 0) { /* memory is copied. */
906
- if (options.length <= BUFFER_PACKET_SIZE) {
907
- /* small enough for internal buffer */
908
- memcpy(packet->buffer.buf, (uint8_t *)options.buffer + options.offset,
909
- options.length);
910
- packet->metadata = (struct packet_metadata_s){
911
- .write_func = sock_write_buffer,
912
- .free_func = (void (*)(packet_s *))SOCK_DEALLOC_NOOP};
913
- goto place_packet_in_queue;
914
- }
915
- /* too big for the pre-allocated buffer */
916
- void *copy = malloc(options.length);
917
- memcpy(copy, (uint8_t *)options.buffer + options.offset, options.length);
918
- options.offset = 0;
919
- options.buffer = copy;
920
- }
921
- /* memory moved, not copied. */
922
- struct sock_packet_ext_data_s *ext = (void *)packet->buffer.buf;
923
- ext->buffer = (uint8_t *)options.buffer + options.offset;
924
- ext->to_free = (uint8_t *)options.buffer;
925
- ext->dealloc = options.dealloc ? options.dealloc : free;
926
- packet->metadata = (struct packet_metadata_s){
927
- .write_func = sock_write_buffer_ext, .free_func = sock_free_buffer_ext};
928
-
929
- } else { /* is file */
930
- struct sock_packet_file_data_s *ext = (void *)packet->buffer.buf;
931
- if (options.is_pfd) {
932
- ext->pfd = (int *)options.buffer;
933
- ext->fd = *ext->pfd;
934
- ext->dealloc = options.dealloc ? options.dealloc : sock_perform_close_pfd;
935
- } else {
936
- ext->fd = options.data_fd;
937
- ext->pfd = NULL;
938
- ext->close = options.close ? options.close : sock_perform_close_fd;
939
- }
940
- ext->offset = options.offset;
941
- packet->metadata = (struct packet_metadata_s){
942
- .write_func =
943
- (fdinfo(sock_uuid2fd(options.uuid)).rw_hooks == &SOCK_DEFAULT_HOOKS
944
- ? sock_sendfile_from_fd
945
- : sock_write_from_fd),
946
- .free_func = options.move ? sock_close_from_fd
947
- : (void (*)(packet_s *))SOCK_DEALLOC_NOOP};
948
- }
1089
+ /* place packet in queue */
949
1090
 
950
- /* place packet in queue */
951
- place_packet_in_queue:
952
- if (validate_uuid(options.uuid))
1091
+ if (validate_uuid(options.uuid) || !options.buffer)
953
1092
  goto error;
954
1093
  lock_fd(fd);
955
1094
  if (!fdinfo(fd).open) {
956
1095
  unlock_fd(fd);
957
1096
  goto error;
958
1097
  }
959
- packet_s **pos = &fdinfo(fd).packet;
960
- if (options.urgent == 0) {
961
- while (*pos)
962
- pos = &(*pos)->metadata.next;
1098
+ packet->next = NULL;
1099
+ if (fdinfo(fd).packet == NULL) {
1100
+ fdinfo(fd).packet_last = &packet->next;
1101
+ fdinfo(fd).packet = packet;
1102
+ } else if (options.urgent == 0) {
1103
+ *fdinfo(fd).packet_last = packet;
1104
+ fdinfo(fd).packet_last = &packet->next;
963
1105
  } else {
964
- if (*pos && fdinfo(fd).sent)
965
- pos = &(*pos)->metadata.next;
966
- packet->metadata.next = *pos;
1106
+ packet_s **pos = &fdinfo(fd).packet;
1107
+ if (*pos)
1108
+ pos = &(*pos)->next;
1109
+ packet->next = *pos;
1110
+ *pos = packet;
1111
+ if (!packet->next) {
1112
+ fdinfo(fd).packet_last = &packet->next;
1113
+ }
967
1114
  }
968
- *pos = packet;
1115
+ ++fdinfo(fd).packet_count;
969
1116
  unlock_fd(fd);
970
1117
  sock_touch(options.uuid);
971
1118
  defer(sock_flush_defer, (void *)options.uuid, NULL);
@@ -979,16 +1126,52 @@ error:
979
1126
  #define sock_write2(...) sock_write2_fn((sock_write_info_s){__VA_ARGS__})
980
1127
 
981
1128
  /**
982
- `sock_flush` writes the data in the internal buffer to the underlying file
983
- descriptor and closes the underlying fd once it's marked for closure (and all
984
- the data was sent).
1129
+ `sock_close` marks the connection for disconnection once all the data was sent.
1130
+ The actual disconnection will be managed by the `sock_flush` function.
1131
+
1132
+ `sock_flash` will automatically be called.
1133
+ */
1134
+ void sock_close(intptr_t uuid) {
1135
+ if (validate_uuid(uuid) || !fdinfo(sock_uuid2fd(uuid)).open)
1136
+ return;
1137
+ fdinfo(sock_uuid2fd(uuid)).close = 1;
1138
+ sock_flush_defer((void *)uuid, (void *)uuid);
1139
+ }
1140
+ /**
1141
+ `sock_force_close` closes the connection immediately, without adhering to any
1142
+ protocol restrictions and without sending any remaining data in the connection
1143
+ buffer.
1144
+ */
1145
+ void sock_force_close(intptr_t uuid) {
1146
+ if (validate_uuid(uuid))
1147
+ return;
1148
+ // fprintf(stderr,
1149
+ // "INFO: (%d) `sock_force_close` called"
1150
+ // " for %p (fd: %u) with errno %d\n",
1151
+ // getpid(), (void *)uuid, (unsigned int)sock_uuid2fd(uuid), errno);
1152
+ // perror("errno");
1153
+ // // We might avoid shutdown, it has side-effects that aren't always clear
1154
+ // shutdown(sock_uuid2fd(uuid), SHUT_RDWR);
1155
+ close(sock_uuid2fd(uuid));
1156
+ clear_fd(sock_uuid2fd(uuid), 0);
1157
+ }
985
1158
 
986
- Return value: 0 will be returned on success and -1 will be returned on an error
987
- or when the connection is closed.
1159
+ /* *****************************************************************************
1160
+ Direct user level buffer API.
988
1161
 
989
- **Please Note**: when using `libreact`, the `sock_flush` will be called
990
- automatically when the socket is ready.
1162
+ The following API allows data to be written directly to the packet, minimizing
1163
+ memory copy operations.
991
1164
  */
1165
+
1166
+ /**
1167
+ * `sock_flush` writes the data in the internal buffer to the underlying file
1168
+ * descriptor and closes the underlying fd once it's marked for closure (and all
1169
+ * the data was sent).
1170
+ *
1171
+ * Return values: 1 will be returned if `sock_flush` should be called again. 0
1172
+ * will be returned if the socket was fully flushed. -1 will be returned on an
1173
+ * error or when the connection is closed.
1174
+ */
992
1175
  ssize_t sock_flush(intptr_t uuid) {
993
1176
  int fd = sock_uuid2fd(uuid);
994
1177
  if (validate_uuid(uuid) || !fdinfo(fd).open)
@@ -997,12 +1180,14 @@ ssize_t sock_flush(intptr_t uuid) {
997
1180
  uint8_t touch = 0;
998
1181
  lock_fd(fd);
999
1182
  sock_rw_hook_s *rw;
1183
+ void *rw_udata;
1000
1184
  retry:
1001
1185
  rw = fdinfo(fd).rw_hooks;
1186
+ rw_udata = fdinfo(fd).rw_udata;
1002
1187
  unlock_fd(fd);
1003
- while ((ret = rw->flush(fd)) > 0)
1004
- if (ret > 0)
1005
- touch = 1;
1188
+ while ((ret = rw->flush(uuid, rw_udata)) > 0) {
1189
+ touch = 1;
1190
+ }
1006
1191
  if (ret == -1) {
1007
1192
  if (errno == EINTR)
1008
1193
  goto retry;
@@ -1012,9 +1197,10 @@ retry:
1012
1197
  goto error;
1013
1198
  }
1014
1199
  lock_fd(fd);
1015
- while (fdinfo(fd).packet && (ret = fdinfo(fd).packet->metadata.write_func(
1016
- fd, fdinfo(fd).packet)) > 0)
1200
+ while (fdinfo(fd).packet &&
1201
+ (ret = fdinfo(fd).packet->write_func(fd, fdinfo(fd).packet)) > 0) {
1017
1202
  touch = 1;
1203
+ }
1018
1204
  if (ret == -1) {
1019
1205
  if (errno == EINTR)
1020
1206
  goto retry;
@@ -1023,22 +1209,25 @@ retry:
1023
1209
  goto finish;
1024
1210
  goto error;
1025
1211
  }
1026
- if (fdinfo(fd).close && !fdinfo(fd).packet)
1212
+ if (!touch && fdinfo(fd).close && !fdinfo(fd).packet)
1027
1213
  goto error;
1028
1214
  finish:
1029
1215
  unlock_fd(fd);
1030
- if (touch)
1216
+ if (touch) {
1031
1217
  sock_touch(uuid);
1032
- return 0;
1218
+ return 1;
1219
+ }
1220
+ return fdinfo(fd).packet != NULL || fdinfo(fd).close;
1033
1221
  error:
1034
1222
  unlock_fd(fd);
1035
1223
  // fprintf(stderr,
1036
- // "ERROR: sock `write` failed"
1224
+ // "ERROR: sock `flush` failed"
1037
1225
  // " for %p with %d\n",
1038
1226
  // (void *)uuid, errno);
1039
1227
  sock_force_close(uuid);
1040
1228
  return -1;
1041
1229
  }
1230
+
1042
1231
  /**
1043
1232
  `sock_flush_strong` performs the same action as `sock_flush` but returns only
1044
1233
  after all the data was sent. This is a "busy" wait, polling isn't performed.
@@ -1048,6 +1237,7 @@ void sock_flush_strong(intptr_t uuid) {
1048
1237
  while (sock_flush(uuid) == 0 && errno == 0)
1049
1238
  ;
1050
1239
  }
1240
+
1051
1241
  /**
1052
1242
  Calls `sock_flush` for each file descriptor that's buffer isn't empty.
1053
1243
  */
@@ -1058,96 +1248,25 @@ void sock_flush_all(void) {
1058
1248
  sock_flush(fd2uuid(fd));
1059
1249
  }
1060
1250
  }
1061
- /**
1062
- `sock_close` marks the connection for disconnection once all the data was sent.
1063
- The actual disconnection will be managed by the `sock_flush` function.
1064
-
1065
- `sock_flash` will automatically be called.
1066
- */
1067
- void sock_close(intptr_t uuid) {
1068
- if (validate_uuid(uuid) || !fdinfo(sock_uuid2fd(uuid)).open)
1069
- return;
1070
- fdinfo(sock_uuid2fd(uuid)).close = 1;
1071
- sock_flush(uuid);
1072
- }
1073
- /**
1074
- `sock_force_close` closes the connection immediately, without adhering to any
1075
- protocol restrictions and without sending any remaining data in the connection
1076
- buffer.
1077
- */
1078
- void sock_force_close(intptr_t uuid) {
1079
- if (validate_uuid(uuid))
1080
- return;
1081
- // fprintf(stderr,
1082
- // "ERROR: `sock_force_close` called"
1083
- // " for %p with errno %d\n",
1084
- // (void *)uuid, errno);
1085
- shutdown(sock_uuid2fd(uuid), SHUT_RDWR);
1086
- close(sock_uuid2fd(uuid));
1087
- clear_fd(sock_uuid2fd(uuid), 0);
1088
- }
1089
-
1090
- /* *****************************************************************************
1091
- Direct user level buffer API.
1092
-
1093
- The following API allows data to be written directly to the packet, minimizing
1094
- memory copy operations.
1095
- */
1096
-
1097
- /**
1098
- Checks out a `sock_buffer_s` from the buffer pool.
1099
- */
1100
- sock_buffer_s *sock_buffer_checkout(void) {
1101
- packet_s *ret = sock_packet_grab();
1102
- return &ret->buffer;
1103
- }
1104
- /**
1105
- Attaches a packet to a socket's output buffer and calls `sock_flush` for the
1106
- socket.
1107
-
1108
- Returns -1 on error. Returns 0 on success. The `buffer` memory is always
1109
- automatically managed.
1110
- */
1111
- ssize_t sock_buffer_send(intptr_t uuid, sock_buffer_s *buffer) {
1112
- if (validate_uuid(uuid) || !fdinfo(sock_uuid2fd(uuid)).open) {
1113
- sock_buffer_free(buffer);
1114
- return -1;
1115
- }
1116
- packet_s **tmp, *packet = (packet_s *)((uintptr_t)(buffer) -
1117
- (uintptr_t)(&((packet_s *)0)->buffer));
1118
- // (packet_s *)((uintptr_t)(buffer) - sizeof(struct packet_metadata_s));
1119
- packet->metadata = (struct packet_metadata_s){
1120
- .write_func = sock_write_buffer,
1121
- .free_func = (void (*)(packet_s *))SOCK_DEALLOC_NOOP};
1122
- int fd = sock_uuid2fd(uuid);
1123
- lock_fd(fd);
1124
- tmp = &fdinfo(fd).packet;
1125
- while (*tmp)
1126
- tmp = &(*tmp)->metadata.next;
1127
- *tmp = packet;
1128
- unlock_fd(fd);
1129
- defer(sock_flush_defer, (void *)uuid, NULL);
1130
- return 0;
1131
- }
1132
1251
 
1133
1252
  /**
1134
- Returns TRUE (non 0) if there is data waiting to be written to the socket in the
1135
- user-land buffer.
1253
+ Returns the number of `sock_write` calls that are waiting in the socket's queue
1254
+ and haven't been processed.
1136
1255
  */
1137
1256
  int sock_has_pending(intptr_t uuid) {
1138
- return validate_uuid(uuid) == 0 && fdinfo(sock_uuid2fd(uuid)).open &&
1139
- fdinfo(sock_uuid2fd(uuid)).packet;
1257
+ if (validate_uuid(uuid) || !uuidinfo(uuid).open)
1258
+ return 0;
1259
+ return (int)(uuidinfo(uuid).packet_count + uuidinfo(uuid).close);
1140
1260
  }
1141
1261
 
1142
1262
  /**
1143
- Use `sock_buffer_free` to free unused buffers that were checked-out using
1144
- `sock_buffer_checkout`.
1145
- */
1146
- void sock_buffer_free(sock_buffer_s *buffer) {
1147
- packet_s *packet =
1148
- (packet_s *)((uintptr_t)(buffer) - (uintptr_t)(&((packet_s *)0)->buffer));
1149
- // (packet_s *)((uintptr_t)(buffer) - sizeof(struct packet_metadata_s));
1150
- sock_packet_free(packet);
1263
+ * Returns the number of `sock_write` calls that are waiting in the socket's
1264
+ * queue and haven't been processed.
1265
+ */
1266
+ size_t sock_pending(intptr_t uuid) {
1267
+ if (validate_uuid(uuid) || !uuidinfo(uuid).open)
1268
+ return 0;
1269
+ return (uuidinfo(uuid).packet_count + uuidinfo(uuid).close);
1151
1270
  }
1152
1271
 
1153
1272
  /* *****************************************************************************
@@ -1158,16 +1277,24 @@ Experimental
1158
1277
 
1159
1278
  /** Gets a socket hook state (a pointer to the struct). */
1160
1279
  struct sock_rw_hook_s *sock_rw_hook_get(intptr_t uuid) {
1161
- if (validate_uuid(uuid) || !fdinfo(sock_uuid2fd(uuid)).open ||
1162
- ((uuid = sock_uuid2fd(uuid)),
1280
+ if (validate_uuid(uuid) || !uuidinfo(uuid).open ||
1281
+ ((void)(uuid = sock_uuid2fd(uuid)),
1163
1282
  fdinfo(uuid).rw_hooks == &SOCK_DEFAULT_HOOKS))
1164
1283
  return NULL;
1165
1284
  return fdinfo(uuid).rw_hooks;
1166
1285
  }
1167
1286
 
1168
- /** Sets a socket hook state (a pointer to the struct). */
1169
- int sock_rw_hook_set(intptr_t uuid, sock_rw_hook_s *rw_hooks) {
1287
+ /** Returns the socket's udata associated with the read/write hook. */
1288
+ void *sock_rw_udata(intptr_t uuid) {
1170
1289
  if (validate_uuid(uuid) || !fdinfo(sock_uuid2fd(uuid)).open)
1290
+ return NULL;
1291
+ uuid = sock_uuid2fd(uuid);
1292
+ return fdinfo(uuid).rw_udata;
1293
+ }
1294
+
1295
+ /** Sets a socket hook state (a pointer to the struct). */
1296
+ int sock_rw_hook_set(intptr_t uuid, sock_rw_hook_s *rw_hooks, void *udata) {
1297
+ if (validate_uuid(uuid) || !uuidinfo(uuid).open)
1171
1298
  return -1;
1172
1299
  if (!rw_hooks->read)
1173
1300
  rw_hooks->read = sock_default_hooks_read;
@@ -1180,6 +1307,7 @@ int sock_rw_hook_set(intptr_t uuid, sock_rw_hook_s *rw_hooks) {
1180
1307
  uuid = sock_uuid2fd(uuid);
1181
1308
  lock_fd(uuid);
1182
1309
  fdinfo(uuid).rw_hooks = rw_hooks;
1310
+ fdinfo(uuid).rw_udata = udata;
1183
1311
  unlock_fd(uuid);
1184
1312
  return 0;
1185
1313
  }
@@ -1189,42 +1317,51 @@ test
1189
1317
  */
1190
1318
  #ifdef DEBUG
1191
1319
  void sock_libtest(void) {
1192
- char request[] = "GET / HTTP/1.1\r\n"
1193
- "Host: www.google.com\r\n"
1194
- "\r\n";
1195
- char buff[1024];
1196
- ssize_t i_read;
1197
- intptr_t uuid = sock_connect("www.google.com", "80");
1198
- if (uuid == -1)
1199
- perror("sock_connect failed"), exit(1);
1200
- if (sock_write(uuid, request, sizeof(request) - 1) < 0)
1201
- perror("sock_write error ");
1202
-
1203
- while ((i_read = sock_read(uuid, buff, 1024)) >= 0) {
1204
- if (i_read == 0) { // could be we hadn't finished connecting yet.
1205
- sock_flush(uuid);
1206
- reschedule_thread();
1207
- } else {
1208
- fprintf(stderr, "\n%.*s\n\n", (int)i_read, buff);
1209
- break;
1320
+ if (0) { /* this test can't be performed witout initializeing `facil`. */
1321
+ char request[] = "GET / HTTP/1.1\r\n"
1322
+ "Host: www.google.com\r\n"
1323
+ "\r\n";
1324
+ char buff[1024];
1325
+ ssize_t i_read;
1326
+ intptr_t uuid = sock_connect("www.google.com", "80");
1327
+ if (uuid == -1) {
1328
+ perror("sock_connect failed");
1329
+ exit(1);
1210
1330
  }
1331
+ if (sock_write(uuid, request, sizeof(request) - 1) < 0)
1332
+ perror("sock_write error ");
1333
+
1334
+ while ((i_read = sock_read(uuid, buff, 1024)) >= 0) {
1335
+ if (i_read == 0) { // could be we hadn't finished connecting yet.
1336
+ sock_flush(uuid);
1337
+ reschedule_thread();
1338
+ } else {
1339
+ fprintf(stderr, "\n%.*s\n\n", (int)i_read, buff);
1340
+ break;
1341
+ }
1342
+ }
1343
+ if (i_read < 0)
1344
+ perror("Error with sock_read ");
1345
+ fprintf(stderr, "done.\n");
1346
+ sock_close(uuid);
1347
+ }
1348
+ sock_max_capacity();
1349
+ for (int i = 0; i < 4; ++i) {
1350
+ packet_s *packet = sock_packet_new();
1351
+ sock_packet_free(packet);
1211
1352
  }
1212
- if (i_read < 0)
1213
- perror("Error with sock_read ");
1214
- fprintf(stderr, "done.\n");
1215
- sock_close(uuid);
1216
1353
  packet_s *head, *pos;
1217
1354
  pos = head = packet_pool.next;
1218
1355
  size_t count = 0;
1219
1356
  while (pos) {
1220
1357
  count++;
1221
- pos = pos->metadata.next;
1358
+ pos = pos->next;
1222
1359
  }
1223
- fprintf(stderr, "Packet pool test %s (%d =? %lu)\n",
1224
- count == BUFFER_PACKET_POOL ? "PASS" : "FAIL", BUFFER_PACKET_POOL,
1225
- count);
1226
- count = sock_max_capacity();
1227
- printf("Allocated sock capacity %lu X %lu\n", count,
1228
- sizeof(struct fd_data_s));
1360
+ fprintf(stderr, "Packet pool test %s (%lu =? %lu)\n",
1361
+ count == BUFFER_PACKET_POOL ? "PASS" : "FAIL",
1362
+ (unsigned long)BUFFER_PACKET_POOL, (unsigned long)count);
1363
+ printf("Allocated sock capacity %lu X %lu\n",
1364
+ (unsigned long)sock_data_store.capacity,
1365
+ (unsigned long)sizeof(struct fd_data_s));
1229
1366
  }
1230
1367
  #endif