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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/redis_engine.h
CHANGED
@@ -1,21 +1,14 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz segev, 2017
|
3
|
-
License: MIT
|
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
|
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
|
45
|
-
value (0) will fallback to facil.io's maximum time of inactivity (5
|
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
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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(
|
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
|
-
}
|
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/
|
26
|
+
#include <sys/ioctl.h>
|
26
27
|
#include <sys/resource.h>
|
27
28
|
#include <sys/socket.h>
|
28
|
-
#include <sys/
|
29
|
+
#include <sys/stat.h>
|
30
|
+
#include <sys/sysctl.h>
|
29
31
|
#include <sys/types.h>
|
30
|
-
#include <
|
32
|
+
#include <sys/un.h>
|
31
33
|
|
32
|
-
#
|
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
|
-
|
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
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
151
|
+
fio_free(packet);
|
133
152
|
}
|
134
153
|
|
135
|
-
static inline packet_s *
|
154
|
+
static inline packet_s *sock_packet_new(void) {
|
136
155
|
packet_s *packet;
|
137
|
-
|
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->
|
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
|
-
|
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].
|
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 *
|
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,
|
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
|
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
|
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->
|
260
|
-
fdinfo(fd).
|
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) {
|
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
|
-
|
294
|
+
goto finish;
|
270
295
|
struct fd_data_s *new_collection =
|
271
|
-
realloc(sock_data_store.fds, sizeof(
|
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
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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 %
|
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
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
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.
|
328
|
-
if (old_data.open
|
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),
|
352
|
-
packet->buffer
|
381
|
+
fd2uuid(fd), fdinfo(fd).rw_udata,
|
382
|
+
((uint8_t *)packet->buffer + packet->offset), packet->length);
|
353
383
|
if (written > 0) {
|
354
|
-
|
355
|
-
|
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
|
-
|
384
|
-
|
385
|
-
|
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) {
|
396
|
-
|
397
|
-
|
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
|
-
|
407
|
-
ssize_t
|
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
|
-
|
410
|
-
packet->
|
412
|
+
packet->offset += sent;
|
413
|
+
packet->length -= sent;
|
411
414
|
retry:
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
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
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
packet->
|
424
|
-
|
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
|
433
|
+
return total;
|
430
434
|
|
431
435
|
read_error:
|
432
|
-
if (
|
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
|
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,
|
449
|
+
sent = sendfile64(fd, packet->fd, &packet->offset, packet->length);
|
449
450
|
if (sent < 0)
|
450
451
|
return -1;
|
451
|
-
packet->
|
452
|
-
if (!packet->
|
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
|
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->
|
464
|
-
act_sent = packet->
|
464
|
+
while (packet->length) {
|
465
|
+
act_sent = packet->length;
|
465
466
|
#if defined(__APPLE__)
|
466
|
-
ret = sendfile(
|
467
|
-
0);
|
467
|
+
ret = sendfile(packet->fd, fd, packet->offset, &act_sent, NULL, 0);
|
468
468
|
#else
|
469
|
-
ret = sendfile(
|
470
|
-
|
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
|
-
|
475
|
-
packet->
|
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
|
-
|
482
|
-
packet->
|
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
|
-
|
519
|
-
static int (
|
520
|
-
|
521
|
-
|
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
|
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
|
-
#
|
565
|
+
#elif defined(FIONBIO)
|
547
566
|
/* Otherwise, use the old way of doing it */
|
548
567
|
static int flags = 1;
|
549
|
-
return ioctl(fd,
|
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(
|
568
|
-
flim =
|
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
|
-
|
574
|
-
|
575
|
-
#if defined(__APPLE__) /* Apple's getrlimit is broken. */
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
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
|
619
|
+
return sock_data_store.capacity;
|
592
620
|
}
|
593
621
|
|
594
622
|
/* *****************************************************************************
|
595
|
-
The main
|
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
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
//
|
636
|
-
|
637
|
-
|
638
|
-
|
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
|
-
|
654
|
-
|
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
|
-
|
705
|
-
|
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
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
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
|
-
|
763
|
-
|
764
|
-
|
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 `
|
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 =
|
806
|
-
.addr = (struct sockaddr *)&
|
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 &&
|
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
|
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 &&
|
873
|
-
|
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
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
960
|
-
if (
|
961
|
-
|
962
|
-
|
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
|
-
|
965
|
-
|
966
|
-
|
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
|
-
|
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
|
-
`
|
983
|
-
|
984
|
-
|
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
|
-
|
987
|
-
|
1159
|
+
/* *****************************************************************************
|
1160
|
+
Direct user level buffer API.
|
988
1161
|
|
989
|
-
|
990
|
-
|
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(
|
1004
|
-
|
1005
|
-
|
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 &&
|
1016
|
-
|
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
|
-
|
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 `
|
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
|
1135
|
-
|
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
|
-
|
1139
|
-
|
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
|
-
|
1144
|
-
|
1145
|
-
*/
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
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) || !
|
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
|
-
/**
|
1169
|
-
|
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
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
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->
|
1358
|
+
pos = pos->next;
|
1222
1359
|
}
|
1223
|
-
fprintf(stderr, "Packet pool test %s (%
|
1224
|
-
count == BUFFER_PACKET_POOL ? "PASS" : "FAIL",
|
1225
|
-
count);
|
1226
|
-
|
1227
|
-
|
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
|