iodine 0.2.17 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +36 -3
- data/bin/config.ru +23 -2
- data/bin/http-hello +1 -1
- data/bin/ws-shootout +5 -0
- data/ext/iodine/defer.c +468 -0
- data/ext/iodine/defer.h +105 -0
- data/ext/iodine/evio.c +263 -0
- data/ext/iodine/evio.h +133 -0
- data/ext/iodine/extconf.rb +2 -1
- data/ext/iodine/facil.c +958 -0
- data/ext/iodine/facil.h +423 -0
- data/ext/iodine/http.c +90 -0
- data/ext/iodine/http.h +50 -12
- data/ext/iodine/http1.c +200 -267
- data/ext/iodine/http1.h +17 -26
- data/ext/iodine/http1_request.c +81 -0
- data/ext/iodine/http1_request.h +58 -0
- data/ext/iodine/http1_response.c +403 -0
- data/ext/iodine/http1_response.h +90 -0
- data/ext/iodine/http1_simple_parser.c +124 -108
- data/ext/iodine/http1_simple_parser.h +8 -3
- data/ext/iodine/http_request.c +104 -0
- data/ext/iodine/http_request.h +58 -102
- data/ext/iodine/http_response.c +212 -208
- data/ext/iodine/http_response.h +89 -252
- data/ext/iodine/iodine_core.c +57 -46
- data/ext/iodine/iodine_core.h +3 -1
- data/ext/iodine/iodine_http.c +105 -81
- data/ext/iodine/iodine_websocket.c +17 -13
- data/ext/iodine/iodine_websocket.h +1 -0
- data/ext/iodine/rb-call.c +9 -7
- data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
- data/ext/iodine/rb-rack-io.c +12 -6
- data/ext/iodine/rb-rack-io.h +1 -1
- data/ext/iodine/rb-registry.c +5 -2
- data/ext/iodine/sock.c +1159 -0
- data/ext/iodine/{libsock.h → sock.h} +138 -142
- data/ext/iodine/spnlock.inc +77 -0
- data/ext/iodine/websockets.c +101 -112
- data/ext/iodine/websockets.h +38 -19
- data/iodine.gemspec +3 -3
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +6 -6
- metadata +23 -19
- data/ext/iodine/http_response_http1.h +0 -382
- data/ext/iodine/libasync.c +0 -570
- data/ext/iodine/libasync.h +0 -122
- data/ext/iodine/libreact.c +0 -350
- data/ext/iodine/libreact.h +0 -244
- data/ext/iodine/libserver.c +0 -957
- data/ext/iodine/libserver.h +0 -481
- data/ext/iodine/libsock.c +0 -1025
- data/ext/iodine/spnlock.h +0 -243
data/ext/iodine/http_response.h
CHANGED
@@ -1,211 +1,78 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
License: MIT
|
1
|
+
#ifndef H_HTTP_RESPONSE_H
|
2
|
+
#define H_HTTP_RESPONSE_H
|
4
3
|
|
5
|
-
Feel free to copy, use and enjoy according to the license provided.
|
6
|
-
*/
|
7
|
-
#ifndef HTTP_RESPONSE
|
8
|
-
/**
|
9
|
-
The HttpResponse library
|
10
|
-
========================
|
11
|
-
|
12
|
-
This library helps us to write HTTP valid responses, even when we do not know
|
13
|
-
the internals of the HTTP protocol.
|
14
|
-
|
15
|
-
The response object allows us to easily update the response status (all
|
16
|
-
responses start with the default 200 "OK" status code), write headers and cookie
|
17
|
-
data to the header buffer and send the response's body.
|
18
|
-
|
19
|
-
The response object also allows us to easily update the body size and send body
|
20
|
-
data or open files (which will be automatically closed once sending is done).
|
21
|
-
|
22
|
-
As example flow for the response could be:
|
23
|
-
|
24
|
-
; // get an initialized HttpRequest object
|
25
|
-
struct HttpRequest * response = HttpResponse.create(request);
|
26
|
-
; // ... write headers and body, i.e.
|
27
|
-
HttpResponse.write_header_cstr(response, "X-Data", "my data");
|
28
|
-
HttpResponse.write_body(response, "Hello World!\r\n", 14);
|
29
|
-
; // release the object
|
30
|
-
HttpResponse.destroy(response);
|
31
|
-
|
32
|
-
|
33
|
-
--
|
34
|
-
Thread-safety:
|
35
|
-
|
36
|
-
The response object and it's API are NOT thread-safe (it is assumed that no two
|
37
|
-
threads handle the same response at the same time).
|
38
|
-
|
39
|
-
Also, the response object will link itself to a libsock buffer packet, so it
|
40
|
-
should be created and dispatched during the same event - `sock_packet_s` objects
|
41
|
-
shouldn't be held across events or for a period of time... In other words:
|
42
|
-
|
43
|
-
**Create the response object only when you are ready to send a response**.
|
44
|
-
|
45
|
-
---
|
46
|
-
Misc notes:
|
47
|
-
The response header's buffer size is limited and too many headers will fail the
|
48
|
-
response.
|
49
|
-
|
50
|
-
The response object allows us to easily update the response status (all
|
51
|
-
responses start with the default 200 "OK" status code), write headers and write
|
52
|
-
cookie data to the header buffer.
|
53
|
-
|
54
|
-
The response object also allows us to easily update the body size and send body
|
55
|
-
data or open files (which will be automatically closed once sending is done).
|
56
|
-
|
57
|
-
The response does NOT support chuncked encoding.
|
58
|
-
|
59
|
-
The following is the response API container, use:
|
60
|
-
|
61
|
-
struct HttpRequest * response = HttpResponse.create(request);
|
62
|
-
|
63
|
-
|
64
|
-
---
|
65
|
-
Performance:
|
66
|
-
|
67
|
-
A note about using this library with the HTTP/1 protocol family (if this library
|
68
|
-
supports HTTP/2, in the future, the use of the response object will be required,
|
69
|
-
as it might not be possible to handle the response manually):
|
70
|
-
|
71
|
-
Since this library safeguards against certain mistakes and manages an
|
72
|
-
internal header buffer, it comes at a performance cost (it adds a layer of data
|
73
|
-
copying to the headers).
|
74
|
-
|
75
|
-
This cost is mitigated by the optional use of a response object pool, so that it
|
76
|
-
actually saves us from using `malloc` for the headers - for some cases this is
|
77
|
-
faster.
|
78
|
-
|
79
|
-
In my performance tests, the greatest issue is this: spliting the headers from
|
80
|
-
the body means that the socket's buffer is under-utilized on the first call to
|
81
|
-
`send`, while sending the headers. While other operations incure minor costs,
|
82
|
-
this is the actual reason for degraded performance when using this library.
|
83
|
-
|
84
|
-
The order of performance should be considered as follows:
|
85
|
-
|
86
|
-
1. Destructive: Overwriting the request's header buffer with both the response
|
87
|
-
headers and the response data (small responses). Sending the data through the
|
88
|
-
socket using the `Server.write` function.
|
89
|
-
|
90
|
-
2. Using malloc to allocate enough memory for both the response's headers AND
|
91
|
-
it's body. Sending the data through the socket using the `Server.write_move`
|
92
|
-
function.
|
93
|
-
|
94
|
-
3. Using the HttpResponse object to send the response.
|
95
|
-
|
96
|
-
Network issues and response properties might influence the order of performant
|
97
|
-
solutions.
|
98
|
-
*/
|
99
|
-
#define HTTP_RESPONSE
|
100
|
-
#include "http.h"
|
101
4
|
#include "http_request.h"
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <time.h>
|
102
7
|
|
103
8
|
typedef struct {
|
104
|
-
/**
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
Set
|
113
|
-
|
114
|
-
|
9
|
+
/** The protocol version family (HTTP/1.1 / HTTP/2 etc'). */
|
10
|
+
enum HTTP_VERSION http_version;
|
11
|
+
/** Will be set to TRUE (1) once the headers were sent. */
|
12
|
+
unsigned headers_sent : 1;
|
13
|
+
/** Set to true when the "Date" header is written to the buffer. */
|
14
|
+
unsigned date_written : 1;
|
15
|
+
/** Set to true when the "Connection" header is written to the buffer. */
|
16
|
+
unsigned connection_written : 1;
|
17
|
+
/** Set to true when the "Content-Length" header is written to the buffer. */
|
18
|
+
unsigned content_length_written : 1;
|
19
|
+
/** Set to true in order to close the connection once the response was sent.
|
20
|
+
*/
|
21
|
+
unsigned should_close : 1;
|
22
|
+
/** Internally used by the logging API. */
|
23
|
+
unsigned logged : 1;
|
24
|
+
/** Set this value to TRUE to indicate the request pointer should be freed. */
|
25
|
+
unsigned request_dupped : 1;
|
26
|
+
/** The response status */
|
27
|
+
uint16_t status;
|
28
|
+
/** The socket UUID for the response. */
|
29
|
+
intptr_t fd;
|
30
|
+
/** The originating request. */
|
31
|
+
http_request_s *request;
|
32
|
+
/** The body's response length.
|
33
|
+
*
|
34
|
+
* If this isn't set manually, the first call to `http_response_write_body`
|
35
|
+
* (and friends) will set the length to the length being written (which might
|
36
|
+
* be less then the total data sent, if the sending is fragmented).
|
37
|
+
*
|
38
|
+
* The value to -1 to prevents `http_response_s` from sending the
|
39
|
+
* `Content-Length` header.
|
40
|
+
*/
|
115
41
|
ssize_t content_length;
|
116
|
-
/**
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
42
|
+
/** The HTTP Date for the response (in seconds since epoche).
|
43
|
+
*
|
44
|
+
* Defaults to now (approximately, not exactly, uses cached time data).
|
45
|
+
*
|
46
|
+
* The date will be automatically formatted to match the HTTP protocol
|
47
|
+
* specifications.
|
48
|
+
*
|
49
|
+
* It is better to avoid setting the "Date" header manualy.
|
50
|
+
*/
|
124
51
|
time_t date;
|
125
|
-
/**
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
52
|
+
/** The HTTP Last-Modified date for the response (in seconds since epoche).
|
53
|
+
*
|
54
|
+
* Defaults to now (approximately, not exactly, uses cached time data).
|
55
|
+
*
|
56
|
+
* The date will be automatically formatted to match the HTTP protocol
|
57
|
+
* specifications.
|
58
|
+
*
|
59
|
+
* It is better to avoid setting the "Last-Modified" header manualy.
|
60
|
+
*/
|
133
61
|
time_t last_modified;
|
134
62
|
/**
|
135
|
-
|
63
|
+
Internally used by the logging API.
|
136
64
|
*/
|
137
|
-
|
138
|
-
/**
|
139
|
-
Metadata about the response's state - don't edit this data (except the opaque
|
140
|
-
data, if needed).
|
141
|
-
*/
|
142
|
-
struct {
|
143
|
-
/**
|
144
|
-
The request object to which this response is "responding".
|
145
|
-
*/
|
146
|
-
http_request_s *request;
|
147
|
-
/**
|
148
|
-
The libsock fd UUID.
|
149
|
-
*/
|
150
|
-
intptr_t fd;
|
151
|
-
/**
|
152
|
-
A `libsock` buffer packet used for header data (to avoid double copy).
|
153
|
-
*/
|
154
|
-
sock_packet_s *packet;
|
155
|
-
/**
|
156
|
-
A pointer to the header's writing position.
|
157
|
-
*/
|
158
|
-
char *headers_pos;
|
159
|
-
/**
|
160
|
-
Internally used by the logging API.
|
161
|
-
*/
|
162
|
-
clock_t clock_start;
|
163
|
-
/**
|
164
|
-
HTTP protocol version identifier.
|
165
|
-
*/
|
166
|
-
uint8_t version;
|
167
|
-
/**
|
168
|
-
Set to true once the headers were sent.
|
169
|
-
*/
|
170
|
-
unsigned headers_sent : 1;
|
171
|
-
/**
|
172
|
-
Set to true when the "Date" header is written to the buffer.
|
173
|
-
*/
|
174
|
-
unsigned date_written : 1;
|
175
|
-
/**
|
176
|
-
Set to true when the "Connection" header is written to the buffer.
|
177
|
-
*/
|
178
|
-
unsigned connection_written : 1;
|
179
|
-
/**
|
180
|
-
Set to true when the "Content-Length" header is written to the buffer.
|
181
|
-
*/
|
182
|
-
unsigned content_length_written : 1;
|
183
|
-
/**
|
184
|
-
Set to true in order to close the connection once the response was sent.
|
185
|
-
*/
|
186
|
-
unsigned should_close : 1;
|
187
|
-
/**
|
188
|
-
Internally used by the logging API.
|
189
|
-
*/
|
190
|
-
unsigned logged : 1;
|
191
|
-
/**
|
192
|
-
Reserved for future use.
|
193
|
-
*/
|
194
|
-
unsigned rsrv : 2;
|
195
|
-
|
196
|
-
} metadata;
|
197
|
-
|
65
|
+
clock_t clock_start;
|
198
66
|
} http_response_s;
|
199
67
|
|
200
68
|
/**
|
201
69
|
The struct HttpCookie is a helper for seting cookie data.
|
202
70
|
|
203
|
-
This struct is used together with the `
|
71
|
+
This struct is used together with the `http_response_set_cookie`. i.e.:
|
204
72
|
|
205
|
-
|
73
|
+
http_response_set_cookie(response,
|
206
74
|
.name = "my_cookie",
|
207
|
-
.value = "data"
|
208
|
-
});
|
75
|
+
.value = "data" );
|
209
76
|
|
210
77
|
*/
|
211
78
|
typedef struct {
|
@@ -233,28 +100,21 @@ typedef struct {
|
|
233
100
|
unsigned http_only : 1;
|
234
101
|
} http_cookie_s;
|
235
102
|
|
236
|
-
|
237
|
-
|
238
|
-
|
103
|
+
/* *****************************************************************************
|
104
|
+
Initialization
|
105
|
+
***************************************************************************** */
|
239
106
|
|
240
|
-
|
241
|
-
|
242
|
-
|
107
|
+
/** Creates / allocates a protocol version's response object. */
|
108
|
+
http_response_s *http_response_create(http_request_s *request);
|
109
|
+
/** Destroys the response object. No data is sent.*/
|
110
|
+
void http_response_destroy(http_response_s *);
|
111
|
+
/** Sends the data and destroys the response object.*/
|
112
|
+
void http_response_finish(http_response_s *);
|
243
113
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
/**
|
248
|
-
Releases any resources held by the response object (doesn't release the response
|
249
|
-
object itself, which might have been allocated on the stack).
|
114
|
+
/* *****************************************************************************
|
115
|
+
Writing data to the response object
|
116
|
+
***************************************************************************** */
|
250
117
|
|
251
|
-
This function assumes the response object might have been stack-allocated.
|
252
|
-
*/
|
253
|
-
void http_response_destroy(http_response_s *response);
|
254
|
-
/** Gets a response status, as a string. */
|
255
|
-
const char *http_response_status_str(uint16_t status);
|
256
|
-
/** Gets the mime-type string (C string) associated with the file extension. */
|
257
|
-
const char *http_response_ext2mime(const char *ext);
|
258
118
|
/**
|
259
119
|
Writes a header to the response. This function writes only the requested
|
260
120
|
number of bytes from the header name and the requested number of bytes from
|
@@ -267,9 +127,9 @@ cannot be sent), the function will return -1.
|
|
267
127
|
|
268
128
|
On success, the function returns 0.
|
269
129
|
*/
|
270
|
-
int
|
130
|
+
int http_response_write_header_fn(http_response_s *, http_header_s header);
|
271
131
|
#define http_response_write_header(response, ...) \
|
272
|
-
|
132
|
+
http_response_write_header_fn(response, (http_header_s){__VA_ARGS__})
|
273
133
|
|
274
134
|
/**
|
275
135
|
Set / Delete a cookie using this helper function.
|
@@ -303,18 +163,6 @@ int http_response_set_cookie(http_response_s *, http_cookie_s);
|
|
303
163
|
#define http_response_set_cookie(response, ...) \
|
304
164
|
http_response_set_cookie(response, (http_cookie_s){__VA_ARGS__})
|
305
165
|
|
306
|
-
/**
|
307
|
-
Indicates that any pending data (i.e. unsent headers) should be sent and that no
|
308
|
-
more use of the response object will be made. This will also release any
|
309
|
-
resources aquired when the response object was initialized, similar to the
|
310
|
-
`http_response_destroy` function.
|
311
|
-
|
312
|
-
If logging was initiated and hadn't been performed, it will be performed.
|
313
|
-
|
314
|
-
If the connection was already closed, the function will return -1. On success,
|
315
|
-
the function returns 0.
|
316
|
-
*/
|
317
|
-
void http_response_finish(http_response_s *);
|
318
166
|
/**
|
319
167
|
Sends the headers (if they weren't previously sent) and writes the data to the
|
320
168
|
underlying socket.
|
@@ -327,33 +175,19 @@ the function returns 0.
|
|
327
175
|
int http_response_write_body(http_response_s *, const char *body,
|
328
176
|
size_t length);
|
329
177
|
|
330
|
-
// /**
|
331
|
-
// REVIEW: IS THIS APPLICABLE FOR HTTP/2 AS WELL? this must be a unified API.
|
332
|
-
//
|
333
|
-
// Sends the headers (if they weren't previously sent) and writes the data to
|
334
|
-
// the
|
335
|
-
// underlying socket.
|
336
|
-
//
|
337
|
-
// The server's outgoing buffer will take ownership of the body and free it's
|
338
|
-
// memory using `free` once the data was sent.
|
339
|
-
//
|
340
|
-
// If the connection was already closed, the function will return -1. On
|
341
|
-
// success,
|
342
|
-
// the function returns 0.
|
343
|
-
// */
|
344
|
-
// int http_response_write_body_move(http_response_s*,
|
345
|
-
// const char* body,
|
346
|
-
// size_t length);
|
347
|
-
|
348
178
|
/**
|
349
179
|
Sends the headers (if they weren't previously sent) and writes the data to the
|
350
180
|
underlying socket.
|
351
181
|
|
352
182
|
The server's outgoing buffer will take ownership of the file and close it
|
353
|
-
using `
|
183
|
+
using `close` once the data was sent.
|
354
184
|
|
355
185
|
If the connection was already closed, the function will return -1. On success,
|
356
|
-
the function returns 0.
|
186
|
+
the function returns 0. The file is alsways closed by the function.
|
187
|
+
|
188
|
+
If should be possible to destroy the response object and send an error response
|
189
|
+
if an error is detected. The function will avoid sending any data before it
|
190
|
+
knows the likelyhood of error is small enough.
|
357
191
|
*/
|
358
192
|
int http_response_sendfile(http_response_s *, int source_fd, off_t offset,
|
359
193
|
size_t length);
|
@@ -387,13 +221,16 @@ int http_response_sendfile2(http_response_s *response, http_request_s *request,
|
|
387
221
|
const char *file_path_safe, size_t path_safe_len,
|
388
222
|
const char *file_path_unsafe,
|
389
223
|
size_t path_unsafe_len, uint8_t log);
|
390
|
-
|
391
|
-
|
392
|
-
*/
|
224
|
+
/* *****************************************************************************
|
225
|
+
Helpers and common tasks
|
226
|
+
***************************************************************************** */
|
227
|
+
|
228
|
+
/** Gets a response status, as a string. */
|
229
|
+
const char *http_response_status_str(uint16_t status);
|
230
|
+
/** Gets the mime-type string (C string) associated with the file extension. */
|
231
|
+
const char *http_response_ext2mime(const char *ext);
|
232
|
+
|
233
|
+
/** Starts counting miliseconds for log results. */
|
393
234
|
void http_response_log_start(http_response_s *);
|
394
|
-
/**
|
395
|
-
prints out the log to stderr.
|
396
|
-
*/
|
397
|
-
void http_response_log_finish(http_response_s *);
|
398
235
|
|
399
236
|
#endif
|
data/ext/iodine/iodine_core.c
CHANGED
@@ -133,7 +133,7 @@ Returns `false` on error and `self` on success.
|
|
133
133
|
*/
|
134
134
|
static VALUE dyn_write_urgent(VALUE self, VALUE data) {
|
135
135
|
intptr_t fd = iodine_get_fd(self);
|
136
|
-
if (sock_write2(.
|
136
|
+
if (sock_write2(.uuid = fd, .buffer = RSTRING(data),
|
137
137
|
.length = RSTRING_LEN(data), .urgent = 1))
|
138
138
|
return Qfalse;
|
139
139
|
return self;
|
@@ -149,7 +149,7 @@ static VALUE dyn_set_timeout(VALUE self, VALUE timeout) {
|
|
149
149
|
unsigned int tout = FIX2UINT(timeout);
|
150
150
|
if (tout > 255)
|
151
151
|
tout = 255;
|
152
|
-
|
152
|
+
facil_set_timeout(fd, tout);
|
153
153
|
return self;
|
154
154
|
}
|
155
155
|
|
@@ -158,7 +158,7 @@ Returns the connection's timeout.
|
|
158
158
|
*/
|
159
159
|
static VALUE dyn_get_timeout(VALUE self) {
|
160
160
|
intptr_t fd = iodine_get_fd(self);
|
161
|
-
uint8_t tout =
|
161
|
+
uint8_t tout = facil_get_timeout(fd);
|
162
162
|
unsigned int tout_int = tout;
|
163
163
|
return UINT2NUM(tout_int);
|
164
164
|
}
|
@@ -211,7 +211,8 @@ static VALUE dyn_defer(VALUE self) {
|
|
211
211
|
return Qfalse;
|
212
212
|
Registry.add(block);
|
213
213
|
intptr_t fd = iodine_get_fd(self);
|
214
|
-
|
214
|
+
facil_defer(.uuid = fd, .task = dyn_perform_defer, .arg = (void *)block,
|
215
|
+
.fallback = dyn_defer_fallback);
|
215
216
|
return self;
|
216
217
|
}
|
217
218
|
|
@@ -221,16 +222,15 @@ static void dyn_perform_each_task(intptr_t fd, protocol_s *protocol,
|
|
221
222
|
RubyCaller.call2((VALUE)data, call_proc_id, 1,
|
222
223
|
&(dyn_prot(protocol)->handler));
|
223
224
|
}
|
224
|
-
static void dyn_finish_each_task(intptr_t fd,
|
225
|
-
void *data) {
|
226
|
-
(void)(protocol);
|
225
|
+
static void dyn_finish_each_task(intptr_t fd, void *data) {
|
227
226
|
(void)(fd);
|
228
227
|
Registry.remove((VALUE)data);
|
229
228
|
}
|
230
229
|
|
231
230
|
void iodine_run_each(intptr_t origin, const char *service, VALUE block) {
|
232
|
-
|
233
|
-
|
231
|
+
facil_each(.origin = origin, .service = service,
|
232
|
+
.task = dyn_perform_each_task, .arg = (void *)block,
|
233
|
+
.on_complete = dyn_finish_each_task);
|
234
234
|
}
|
235
235
|
|
236
236
|
/**
|
@@ -252,8 +252,9 @@ static VALUE dyn_each(VALUE self) {
|
|
252
252
|
return Qfalse;
|
253
253
|
Registry.add(block);
|
254
254
|
intptr_t fd = iodine_get_fd(self);
|
255
|
-
|
256
|
-
|
255
|
+
facil_each(.origin = fd, .service = iodine_protocol_service,
|
256
|
+
.task = dyn_perform_each_task, .arg = (void *)block,
|
257
|
+
.on_complete = dyn_finish_each_task);
|
257
258
|
return self;
|
258
259
|
}
|
259
260
|
|
@@ -275,8 +276,9 @@ static VALUE dyn_class_each(VALUE self) {
|
|
275
276
|
if (block == Qnil)
|
276
277
|
return Qfalse;
|
277
278
|
Registry.add(block);
|
278
|
-
|
279
|
-
|
279
|
+
facil_each(.origin = -1, .service = iodine_protocol_service,
|
280
|
+
.task = dyn_perform_each_task, .arg = (void *)block,
|
281
|
+
.on_complete = dyn_finish_each_task);
|
280
282
|
return self;
|
281
283
|
}
|
282
284
|
|
@@ -367,7 +369,7 @@ static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
|
|
367
369
|
Registry.remove(handler);
|
368
370
|
return NULL;
|
369
371
|
}
|
370
|
-
|
372
|
+
facil_set_timeout(fduuid, timeout);
|
371
373
|
*protocol = (dyn_protocol_s){
|
372
374
|
.handler = handler,
|
373
375
|
.protocol.on_data = dyn_protocol_on_data,
|
@@ -456,11 +458,12 @@ static VALUE iodine_listen_dyn_protocol(VALUE self, VALUE port, VALUE handler) {
|
|
456
458
|
rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
|
457
459
|
if (TYPE(port) == T_FIXNUM)
|
458
460
|
port = rb_funcall2(port, to_s_method_id, 0, NULL);
|
461
|
+
rb_ivar_set(self, rb_intern("_port"), port);
|
459
462
|
// listen
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
463
|
+
facil_listen(.port = StringValueCStr(port), .udata = (void *)handler,
|
464
|
+
.on_open = on_open_dyn_protocol,
|
465
|
+
.on_start = on_server_start_for_handler,
|
466
|
+
.on_finish = on_server_on_finish_for_handler);
|
464
467
|
return self;
|
465
468
|
}
|
466
469
|
|
@@ -489,7 +492,7 @@ VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler) {
|
|
489
492
|
}
|
490
493
|
protocol_s *protocol = dyn_set_protocol(fduuid, handler, timeout);
|
491
494
|
if (protocol) {
|
492
|
-
if (
|
495
|
+
if (facil_attach(fduuid, protocol))
|
493
496
|
dyn_protocol_on_close(protocol);
|
494
497
|
return handler;
|
495
498
|
}
|
@@ -500,6 +503,11 @@ VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler) {
|
|
500
503
|
Iodine Task Management
|
501
504
|
*/
|
502
505
|
|
506
|
+
static void iodine_run_once2(void *block, void *ignr) {
|
507
|
+
(void)ignr;
|
508
|
+
RubyCaller.call((VALUE)block, call_proc_id);
|
509
|
+
Registry.remove((VALUE)block);
|
510
|
+
}
|
503
511
|
static void iodine_run_once(void *block) {
|
504
512
|
RubyCaller.call((VALUE)block, call_proc_id);
|
505
513
|
Registry.remove((VALUE)block);
|
@@ -525,10 +533,8 @@ static VALUE iodine_run_async(VALUE self) {
|
|
525
533
|
if (block == Qnil)
|
526
534
|
return Qfalse;
|
527
535
|
Registry.add(block);
|
528
|
-
if (
|
529
|
-
|
530
|
-
;
|
531
|
-
}
|
536
|
+
if (defer(iodine_run_once2, (void *)block, NULL))
|
537
|
+
perror("ERROR: dropped defered task");
|
532
538
|
return block;
|
533
539
|
}
|
534
540
|
|
@@ -551,7 +557,7 @@ static VALUE iodine_run_after(VALUE self, VALUE milliseconds) {
|
|
551
557
|
if (block == Qnil)
|
552
558
|
return Qfalse;
|
553
559
|
Registry.add(block);
|
554
|
-
|
560
|
+
facil_run_every(milli, 1, iodine_run_once, (void *)block, NULL);
|
555
561
|
return block;
|
556
562
|
}
|
557
563
|
/**
|
@@ -591,19 +597,23 @@ static VALUE iodine_run_every(int argc, VALUE *argv, VALUE self) {
|
|
591
597
|
// requires a block to be passed
|
592
598
|
rb_need_block();
|
593
599
|
Registry.add(block);
|
594
|
-
|
595
|
-
|
600
|
+
facil_run_every(milli, repeat, iodine_run_always, (void *)block,
|
601
|
+
(void (*)(void *))Registry.remove);
|
596
602
|
return block;
|
597
603
|
}
|
598
604
|
|
599
605
|
static VALUE iodine_count(VALUE self) {
|
600
606
|
(void)(self);
|
601
|
-
return ULONG2NUM(
|
607
|
+
return ULONG2NUM(facil_count(NULL));
|
602
608
|
}
|
603
609
|
/* *****************************************************************************
|
604
610
|
Running the server
|
605
611
|
*/
|
606
|
-
|
612
|
+
#include <pthread.h>
|
613
|
+
|
614
|
+
static volatile int sock_io_thread = 0;
|
615
|
+
static pthread_t sock_io_pthread;
|
616
|
+
|
607
617
|
static void *iodine_io_thread(void *arg) {
|
608
618
|
(void)arg;
|
609
619
|
static const struct timespec tm = {.tv_nsec = 16777216UL};
|
@@ -613,10 +623,14 @@ static void *iodine_io_thread(void *arg) {
|
|
613
623
|
}
|
614
624
|
return NULL;
|
615
625
|
}
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
pthread_create(&
|
626
|
+
static void iodine_start_io_thread(void *a1, void *a2) {
|
627
|
+
(void)a1;
|
628
|
+
(void)a2;
|
629
|
+
pthread_create(&sock_io_pthread, NULL, iodine_io_thread, NULL);
|
630
|
+
}
|
631
|
+
static void iodine_join_io_thread(void) {
|
632
|
+
sock_io_thread = 0;
|
633
|
+
pthread_join(sock_io_pthread, NULL);
|
620
634
|
}
|
621
635
|
|
622
636
|
static void *srv_start_no_gvl(void *_) {
|
@@ -630,12 +644,13 @@ static void *srv_start_no_gvl(void *_) {
|
|
630
644
|
#ifdef _SC_NPROCESSORS_ONLN
|
631
645
|
size_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
|
632
646
|
if (processes <= 0)
|
633
|
-
processes =
|
647
|
+
processes = 0;
|
634
648
|
if (threads <= 0)
|
635
|
-
threads =
|
649
|
+
threads = 0;
|
636
650
|
|
637
|
-
if (
|
638
|
-
|
651
|
+
if (processes && threads && cpu_count > 0 &&
|
652
|
+
(((size_t)processes << 1) < cpu_count ||
|
653
|
+
(size_t)processes > (cpu_count << 1)))
|
639
654
|
fprintf(
|
640
655
|
stderr,
|
641
656
|
"* Performance warnning:\n"
|
@@ -650,21 +665,17 @@ static void *srv_start_no_gvl(void *_) {
|
|
650
665
|
cpu_count, cpu_count);
|
651
666
|
#else
|
652
667
|
if (processes <= 0)
|
653
|
-
processes =
|
668
|
+
processes = 0;
|
654
669
|
if (threads <= 0)
|
655
|
-
threads =
|
670
|
+
threads = 0;
|
656
671
|
#endif
|
657
672
|
sock_io_thread = 1;
|
658
|
-
|
659
|
-
|
660
|
-
|
673
|
+
defer(iodine_start_io_thread, NULL, NULL);
|
674
|
+
facil_run(.threads = threads, .processes = processes,
|
675
|
+
.on_finish = iodine_join_io_thread);
|
661
676
|
return NULL;
|
662
677
|
}
|
663
678
|
|
664
|
-
static void unblck(void *_) {
|
665
|
-
(void)(_);
|
666
|
-
server_stop();
|
667
|
-
}
|
668
679
|
/**
|
669
680
|
Starts the Iodine event loop. This will hang the thread until an interrupt
|
670
681
|
(`^C`) signal is received.
|
@@ -676,7 +687,7 @@ static VALUE iodine_start(VALUE self) {
|
|
676
687
|
perror("Iodine couldn't start HTTP service... port busy? ");
|
677
688
|
return Qnil;
|
678
689
|
}
|
679
|
-
rb_thread_call_without_gvl2(srv_start_no_gvl, (void *)self,
|
690
|
+
rb_thread_call_without_gvl2(srv_start_no_gvl, (void *)self, NULL, NULL);
|
680
691
|
|
681
692
|
return self;
|
682
693
|
}
|