iodine 0.4.6 → 0.4.7
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 +12 -0
- data/README.md +3 -1
- data/examples/config.ru +56 -0
- data/ext/iodine/defer.c +0 -1
- data/ext/iodine/defer.h +1 -1
- data/ext/iodine/evio.h +1 -1
- data/ext/iodine/facil.c +23 -6
- data/ext/iodine/facil.h +18 -0
- data/ext/iodine/http.h +1 -3
- data/ext/iodine/http1.c +102 -108
- data/ext/iodine/http1_simple_parser.c +3 -3
- data/ext/iodine/http_request.h +1 -1
- data/ext/iodine/http_response.c +16 -5
- data/ext/iodine/misc.c +3 -2
- data/ext/iodine/pubsub.c +1 -1
- data/ext/iodine/pubsub.h +1 -1
- data/ext/iodine/resp.h +2 -3
- data/ext/iodine/sock.c +6 -1
- data/ext/iodine/sock.h +11 -3
- data/ext/iodine/websockets.c +10 -4
- data/lib/iodine/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 481aeb644c0f6d2b38f2c00bc98412cc867e77d5
|
4
|
+
data.tar.gz: bd3eebc92151a3f2d4f41456c6d9402e7cc005fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f31a4c09c7cbfe4df0a9f1df10c9c12004c7e92e3a2cbe1b513afff12c4c5670a457e0f4f6e8cf710ae5cb677d6b3c319f30baac802aefac054f8c8ccda058b
|
7
|
+
data.tar.gz: 9332123f1197af323c1b1af3a5fb1c5bbce7619b6586c1fb6ca049d220d9ebe6780637f5212588b55552e1bf17a07823cf5aedd1062dbf072095b1418df71cd5
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,18 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
6
6
|
|
7
7
|
## Changes:
|
8
8
|
|
9
|
+
#### Change log v.0.4.7
|
10
|
+
|
11
|
+
**Update**: Now using `facil.io` edge (stripped down v.0.5.3).
|
12
|
+
|
13
|
+
**Fix**: (`websocket`) fix #21, where a client's first message could have been lost due to long `on_open` processing times. This was fixed by fragmenting the `upgrade` event into two events, adding the `facil_attach_locked` feature and attaching the new protocol before sending the response. Credit to @madsheep and @nilclass for exposing the issue and tracking it down to the `on_open` callbacks.
|
14
|
+
|
15
|
+
**Fix**: (`sock`) sockets created using the TCP/IP `sock` library now use `TCP_NODELAY` as the new default. This shouldn't be considered a breaking change as much as it should be considered a fix.
|
16
|
+
|
17
|
+
**Fix**: (`http1`) HTTP/1.x now correctly initializes the `udata` pointer to NULL fore each new request.
|
18
|
+
|
19
|
+
**Fix**: (`defer`) a shutdown issue in `defer_perform_in_fork` was detected by @cdkrot and his fix was implemented.
|
20
|
+
|
9
21
|
#### Change log v.0.4.6
|
10
22
|
|
11
23
|
**Update**: Now using `facil.io` v.0.5.2.
|
data/README.md
CHANGED
@@ -397,7 +397,9 @@ Yes, please, here are some thoughts:
|
|
397
397
|
|
398
398
|
* I'm really not good at writing automated tests and benchmarks, any help would be appreciated. I keep testing manually and that's less then ideal (and it's mistake prone).
|
399
399
|
|
400
|
-
* If we can write a Java wrapper for [the C
|
400
|
+
* If we can write a Java wrapper for [the `facil.io` C framework](https://github.com/boazsegev/facil.io), it would be nice... but it could be as big a project as the whole gem, as a lot of minor details are implemented within the bridge between these two languages.
|
401
|
+
|
402
|
+
* PRs or issues related to [the `facil.io` C framework](https://github.com/boazsegev/facil.io) would be better placed in [the `facil.io` repository](https://github.com/boazsegev/facil.io).
|
401
403
|
|
402
404
|
* Bug reports and pull requests are welcome on GitHub at https://github.com/boazsegev/iodine.
|
403
405
|
|
data/examples/config.ru
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# This is a Websocket echo application.
|
2
|
+
#
|
3
|
+
# Running this application from the command line is eacy with:
|
4
|
+
#
|
5
|
+
# iodine hello.ru
|
6
|
+
#
|
7
|
+
# Or, in single thread and single process:
|
8
|
+
#
|
9
|
+
# iodine -t 1 -w 1 hello.ru
|
10
|
+
#
|
11
|
+
# Benchmark with `ab` or `wrk` (a 5 seconds benchmark with 2000 concurrent clients):
|
12
|
+
#
|
13
|
+
# ab -c 2000 -t 5 -n 1000000 -k http://127.0.0.1:3000/
|
14
|
+
# wrk -c2000 -d5 -t12 http://localhost:3000/
|
15
|
+
|
16
|
+
|
17
|
+
# A simple router - Checks for Websocket Upgrade and answers HTTP.
|
18
|
+
module MyHTTPRouter
|
19
|
+
# This is the HTTP response object according to the Rack specification.
|
20
|
+
HTTP_RESPONSE = [200, { 'Content-Type' => 'text/html',
|
21
|
+
'Content-Length' => '32' },
|
22
|
+
['Please connect using websockets.']]
|
23
|
+
|
24
|
+
WS_RESPONSE = [0, {}, []].freeze
|
25
|
+
|
26
|
+
# this is function will be called by the Rack server (iodine) for every request.
|
27
|
+
def self.call env
|
28
|
+
# check if this is an upgrade request.
|
29
|
+
if(env['upgrade.websocket?'.freeze])
|
30
|
+
env['upgrade.websocket'.freeze] = WebsocketEcho
|
31
|
+
return [0, {}, []]
|
32
|
+
return WS_RESPONSE
|
33
|
+
end
|
34
|
+
# simply return the RESPONSE object, no matter what request was received.
|
35
|
+
HTTP_RESPONSE
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# A simple Websocket Callback Object.
|
40
|
+
class WebsocketEcho
|
41
|
+
# seng a message to new clients.
|
42
|
+
def on_open
|
43
|
+
write "Echo service initiated."
|
44
|
+
end
|
45
|
+
# send a message, letting the client know the server is suggunt down.
|
46
|
+
def on_shutdown
|
47
|
+
write "Server shutting down. Goodbye."
|
48
|
+
end
|
49
|
+
# perform the echo
|
50
|
+
def on_message data
|
51
|
+
write data
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# this function call links our HelloWorld application with Rack
|
56
|
+
run MyHTTPRouter
|
data/ext/iodine/defer.c
CHANGED
data/ext/iodine/defer.h
CHANGED
data/ext/iodine/evio.h
CHANGED
data/ext/iodine/facil.c
CHANGED
@@ -505,6 +505,7 @@ static void connector_on_close(intptr_t uuid, protocol_s *pconnector) {
|
|
505
505
|
|
506
506
|
#undef facil_connect
|
507
507
|
intptr_t facil_connect(struct facil_connect_args opt) {
|
508
|
+
intptr_t uuid = -1;
|
508
509
|
if (!opt.address || !opt.port || !opt.on_connect)
|
509
510
|
goto error;
|
510
511
|
if (!opt.set_rw_hooks)
|
@@ -524,7 +525,7 @@ intptr_t facil_connect(struct facil_connect_args opt) {
|
|
524
525
|
.rw_udata = opt.rw_udata,
|
525
526
|
.opened = 0,
|
526
527
|
};
|
527
|
-
|
528
|
+
uuid = connector->uuid = sock_connect(opt.address, opt.port);
|
528
529
|
/* check for errors, always invoke the on_fail if required */
|
529
530
|
if (uuid == -1)
|
530
531
|
goto error;
|
@@ -1080,10 +1081,8 @@ void facil_run(struct facil_run_args args) {
|
|
1080
1081
|
Setting the protocol
|
1081
1082
|
***************************************************************************** */
|
1082
1083
|
|
1083
|
-
|
1084
|
-
|
1085
|
-
*/
|
1086
|
-
int facil_attach(intptr_t uuid, protocol_s *protocol) {
|
1084
|
+
static int facil_attach_state(intptr_t uuid, protocol_s *protocol,
|
1085
|
+
protocol_metadata_s state) {
|
1087
1086
|
if (!facil_data)
|
1088
1087
|
facil_lib_init();
|
1089
1088
|
if (protocol) {
|
@@ -1097,7 +1096,7 @@ int facil_attach(intptr_t uuid, protocol_s *protocol) {
|
|
1097
1096
|
protocol->ping = mock_ping;
|
1098
1097
|
if (!protocol->on_shutdown)
|
1099
1098
|
protocol->on_shutdown = mock_on_ev;
|
1100
|
-
protocol
|
1099
|
+
prt_meta(protocol) = state;
|
1101
1100
|
}
|
1102
1101
|
spn_lock(&uuid_data(uuid).lock);
|
1103
1102
|
if (!sock_isvalid(uuid)) {
|
@@ -1117,6 +1116,24 @@ int facil_attach(intptr_t uuid, protocol_s *protocol) {
|
|
1117
1116
|
return 0;
|
1118
1117
|
}
|
1119
1118
|
|
1119
|
+
/** Attaches (or updates) a protocol object to a socket UUID.
|
1120
|
+
* Returns -1 on error and 0 on success.
|
1121
|
+
*/
|
1122
|
+
int facil_attach(intptr_t uuid, protocol_s *protocol) {
|
1123
|
+
return facil_attach_state(uuid, protocol, (protocol_metadata_s){.rsv = 0});
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
/**
|
1127
|
+
* Attaches (or updates) a LOCKED protocol object to a socket UUID.
|
1128
|
+
*/
|
1129
|
+
int facil_attach_locked(intptr_t uuid, protocol_s *protocol) {
|
1130
|
+
{
|
1131
|
+
protocol_metadata_s state = {.rsv = 0};
|
1132
|
+
spn_lock(state.locks + FIO_PR_LOCK_TASK);
|
1133
|
+
return facil_attach_state(uuid, protocol, state);
|
1134
|
+
}
|
1135
|
+
}
|
1136
|
+
|
1120
1137
|
/** Sets a timeout for a specific connection (if active). */
|
1121
1138
|
void facil_set_timeout(intptr_t uuid, uint8_t timeout) {
|
1122
1139
|
if (sock_isvalid(uuid)) {
|
data/ext/iodine/facil.h
CHANGED
@@ -284,6 +284,7 @@ struct facil_run_args {
|
|
284
284
|
*/
|
285
285
|
void facil_run(struct facil_run_args args);
|
286
286
|
#define facil_run(...) facil_run((struct facil_run_args){__VA_ARGS__})
|
287
|
+
|
287
288
|
/**
|
288
289
|
* Attaches (or updates) a protocol object to a socket UUID.
|
289
290
|
*
|
@@ -298,6 +299,23 @@ void facil_run(struct facil_run_args args);
|
|
298
299
|
*/
|
299
300
|
int facil_attach(intptr_t uuid, protocol_s *protocol);
|
300
301
|
|
302
|
+
/**
|
303
|
+
* Attaches (or updates) a LOCKED protocol object to a socket UUID.
|
304
|
+
*
|
305
|
+
* The protocol will be attached in the FIO_PR_LOCK_TASK state, requiring a
|
306
|
+
* furthur call to `facil_protocol_unlock`.
|
307
|
+
*
|
308
|
+
* The new protocol object can be NULL, which will practically "hijack" the
|
309
|
+
* socket away from facil.
|
310
|
+
*
|
311
|
+
* The old protocol's `on_close` will be scheduled, if they both exist.
|
312
|
+
*
|
313
|
+
* Returns -1 on error and 0 on success.
|
314
|
+
*
|
315
|
+
* On error, the new protocol's `on_close` callback will be called.
|
316
|
+
*/
|
317
|
+
int facil_attach_locked(intptr_t uuid, protocol_s *protocol);
|
318
|
+
|
301
319
|
/** Sets a timeout for a specific connection (if active). */
|
302
320
|
void facil_set_timeout(intptr_t uuid, uint8_t timeout);
|
303
321
|
|
data/ext/iodine/http.h
CHANGED
@@ -6,9 +6,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
6
6
|
*/
|
7
7
|
#ifndef HTTP_H
|
8
8
|
#define HTTP_H
|
9
|
-
|
10
|
-
#define LIB_FACIL_HTTP_VERSION_MINOR 4
|
11
|
-
#define LIB_FACIL_HTTP_VERSION_PATCH 0
|
9
|
+
|
12
10
|
#include "facil.h"
|
13
11
|
|
14
12
|
/** an HTTP/1.1 vs. HTTP/2 identifier. */
|
data/ext/iodine/http1.c
CHANGED
@@ -117,121 +117,115 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *pr) {
|
|
117
117
|
char buff[HTTP_BODY_CHUNK_SIZE];
|
118
118
|
http1_request_s *request = &pr->request;
|
119
119
|
char *buffer = request->buffer;
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
// }
|
140
|
-
}
|
141
|
-
if (pr->len <= 0)
|
142
|
-
goto finished_reading;
|
143
|
-
|
144
|
-
// parse headers
|
145
|
-
result =
|
146
|
-
http1_parse_request_headers(buffer, request->buffer_pos,
|
147
|
-
&request->request, http1_on_header_found);
|
148
|
-
// review result
|
149
|
-
if (result >= 0) { // headers comeplete
|
150
|
-
// are we done?
|
151
|
-
if (request->request.content_length == 0 || request->request.body_str) {
|
152
|
-
goto handle_request;
|
153
|
-
}
|
154
|
-
if (request->request.content_length > pr->settings->max_body_size) {
|
155
|
-
goto body_to_big;
|
156
|
-
}
|
157
|
-
// initialize or submit body data
|
158
|
-
result = http1_parse_request_body(buffer + result, pr->len - result,
|
159
|
-
(http_request_s *)request);
|
160
|
-
if (result >= 0) {
|
161
|
-
request->buffer_pos += result;
|
162
|
-
goto handle_request;
|
163
|
-
} else if (result == -1) // parser error
|
164
|
-
goto parser_error;
|
165
|
-
goto parse_body;
|
166
|
-
} else if (result == -1) // parser error
|
167
|
-
goto parser_error;
|
168
|
-
// assume incomplete (result == -2), even if wrong, we're right.
|
169
|
-
pr->len = 0;
|
170
|
-
continue;
|
120
|
+
// handle requests with no file data
|
121
|
+
if (request->request.body_file <= 0) {
|
122
|
+
// request headers parsing
|
123
|
+
if (pr->len == 0) {
|
124
|
+
buffer = request->buffer;
|
125
|
+
// make sure headers don't overflow
|
126
|
+
pr->len = sock_read(uuid, buffer + request->buffer_pos,
|
127
|
+
HTTP1_MAX_HEADER_SIZE - request->buffer_pos);
|
128
|
+
// update buffer read position.
|
129
|
+
request->buffer_pos += pr->len;
|
130
|
+
// if (len > 0) {
|
131
|
+
// fprintf(stderr, "\n----\nRead from socket, %lu bytes, total
|
132
|
+
// %lu:\n",
|
133
|
+
// len, request->buffer_pos);
|
134
|
+
// for (size_t i = 0; i < request->buffer_pos; i++) {
|
135
|
+
// fprintf(stderr, "%c", buffer[i] ? buffer[i] : '-');
|
136
|
+
// }
|
137
|
+
// fprintf(stderr, "\n");
|
138
|
+
// }
|
171
139
|
}
|
172
|
-
if (
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
140
|
+
if (pr->len <= 0)
|
141
|
+
goto finished_reading;
|
142
|
+
|
143
|
+
// parse headers
|
144
|
+
result = http1_parse_request_headers(
|
145
|
+
buffer, request->buffer_pos, &request->request, http1_on_header_found);
|
146
|
+
// review result
|
147
|
+
if (result >= 0) { // headers comeplete
|
148
|
+
// are we done?
|
149
|
+
if (request->request.content_length == 0 || request->request.body_str) {
|
150
|
+
goto handle_request;
|
151
|
+
}
|
152
|
+
if (request->request.content_length > pr->settings->max_body_size) {
|
153
|
+
goto body_to_big;
|
154
|
+
}
|
155
|
+
// initialize or submit body data
|
156
|
+
result = http1_parse_request_body(buffer + result, pr->len - result,
|
157
|
+
(http_request_s *)request);
|
181
158
|
if (result >= 0) {
|
159
|
+
request->buffer_pos += result;
|
182
160
|
goto handle_request;
|
183
161
|
} else if (result == -1) // parser error
|
184
162
|
goto parser_error;
|
185
|
-
if (pr->len < HTTP_BODY_CHUNK_SIZE) // pause parser for more data
|
186
|
-
goto finished_reading;
|
187
163
|
goto parse_body;
|
188
|
-
}
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
}
|
206
|
-
|
207
|
-
if (
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
}
|
164
|
+
} else if (result == -1) // parser error
|
165
|
+
goto parser_error;
|
166
|
+
// assume incomplete (result == -2), even if wrong, we're right.
|
167
|
+
pr->len = 0;
|
168
|
+
goto postpone;
|
169
|
+
}
|
170
|
+
if (request->request.body_file > 0) {
|
171
|
+
// fprintf(stderr, "Body File\n");
|
172
|
+
parse_body:
|
173
|
+
buffer = buff;
|
174
|
+
// request body parsing
|
175
|
+
pr->len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
|
176
|
+
if (pr->len <= 0)
|
177
|
+
goto finished_reading;
|
178
|
+
result = http1_parse_request_body(buffer, pr->len, &request->request);
|
179
|
+
if (result >= 0) {
|
180
|
+
goto handle_request;
|
181
|
+
} else if (result == -1) // parser error
|
182
|
+
goto parser_error;
|
183
|
+
if (pr->len < HTTP_BODY_CHUNK_SIZE) // pause parser for more data
|
184
|
+
goto finished_reading;
|
185
|
+
goto parse_body;
|
186
|
+
}
|
187
|
+
goto postpone;
|
188
|
+
handle_request:
|
189
|
+
// review required headers / data
|
190
|
+
if (request->request.host == NULL)
|
191
|
+
goto bad_request;
|
192
|
+
http_settings_s *settings = pr->settings;
|
193
|
+
request->request.settings = settings;
|
194
|
+
// make sure udata to NULL, making it available for the user
|
195
|
+
request->request.udata = NULL;
|
196
|
+
// call request callback
|
197
|
+
if (pr && settings &&
|
198
|
+
(request->request.upgrade || settings->public_folder == NULL ||
|
199
|
+
http_response_sendfile2(NULL, &request->request, settings->public_folder,
|
200
|
+
settings->public_folder_length,
|
201
|
+
request->request.path, request->request.path_len,
|
202
|
+
settings->log_static))) {
|
203
|
+
pr->on_request(&request->request);
|
204
|
+
// fprintf(stderr, "Called on_request\n");
|
230
205
|
}
|
231
|
-
//
|
232
|
-
|
233
|
-
|
234
|
-
|
206
|
+
// rotate buffer for HTTP pipelining
|
207
|
+
if ((ssize_t)request->buffer_pos <= result) {
|
208
|
+
pr->len = 0;
|
209
|
+
// fprintf(stderr, "\n----\nAll data consumed.\n");
|
210
|
+
} else {
|
211
|
+
memmove(request->buffer, buffer + result, request->buffer_pos - result);
|
212
|
+
pr->len = request->buffer_pos - result;
|
213
|
+
// fprintf(stderr, "\n----\ndata after move, %lu long:\n%.*s\n", len,
|
214
|
+
// (int)len, request->buffer);
|
215
|
+
}
|
216
|
+
// fprintf(stderr, "data in buffer, %lu long:\n%.*s\n", len, (int)len,
|
217
|
+
// request->buffer);
|
218
|
+
// clear request state
|
219
|
+
http1_request_clear(&request->request);
|
220
|
+
request->buffer_pos = pr->len;
|
221
|
+
// make sure to use the correct buffer.
|
222
|
+
buffer = request->buffer;
|
223
|
+
/* prevent this connection from hogging the thread by pipelining endless
|
224
|
+
* requests.
|
225
|
+
*/
|
226
|
+
postpone:
|
227
|
+
facil_force_event(uuid, FIO_EVENT_ON_DATA);
|
228
|
+
return;
|
235
229
|
parser_error:
|
236
230
|
if (request->request.headers_count >= HTTP1_MAX_HEADER_COUNT)
|
237
231
|
goto too_big;
|
@@ -358,7 +358,7 @@ ssize_t http1_parse_request_body(void *buffer, size_t len,
|
|
358
358
|
request->body_file = mkstemp(template);
|
359
359
|
if (request->body_file == -1)
|
360
360
|
return -1;
|
361
|
-
// use the `
|
361
|
+
// use the `udata` field to store parser state.
|
362
362
|
uintptr_t *tmp = (uintptr_t *)(&request->udata);
|
363
363
|
*tmp = 0;
|
364
364
|
}
|
@@ -373,9 +373,9 @@ ssize_t http1_parse_request_body(void *buffer, size_t len,
|
|
373
373
|
// write the data to the temporary file.
|
374
374
|
if (write(request->body_file, buffer, to_read) < to_read)
|
375
375
|
return -1;
|
376
|
-
// update the `
|
376
|
+
// update the `udata` field data with the received content length
|
377
377
|
uintptr_t *tmp = (uintptr_t *)(&request->udata);
|
378
|
-
*tmp += to_read; // request->metadata.
|
378
|
+
*tmp += to_read; // (uintptr_t)(request->metadata.udata) += to_read;
|
379
379
|
|
380
380
|
// check the state and return.
|
381
381
|
if (((uintptr_t)request->udata) >= request->content_length) {
|
data/ext/iodine/http_request.h
CHANGED
@@ -22,7 +22,7 @@ struct http_request_s {
|
|
22
22
|
const http_settings_s *settings;
|
23
23
|
/** the HTTP connection identifier. */
|
24
24
|
intptr_t fd;
|
25
|
-
/** this is an opaque pointer,
|
25
|
+
/** this is an opaque pointer, doubles for request pooling / chaining */
|
26
26
|
void *udata;
|
27
27
|
/** points to the HTTP method name. */
|
28
28
|
const char *method;
|
data/ext/iodine/http_response.c
CHANGED
@@ -475,9 +475,8 @@ void http_response_log_start(http_response_s *response) {
|
|
475
475
|
prints out the log to stderr.
|
476
476
|
*/
|
477
477
|
static void http_response_log_finish(http_response_s *response) {
|
478
|
-
#define HTTP_REQUEST_LOG_LIMIT 128
|
478
|
+
#define HTTP_REQUEST_LOG_LIMIT 128 /* Log message length limit */
|
479
479
|
char buffer[HTTP_REQUEST_LOG_LIMIT];
|
480
|
-
|
481
480
|
http_request_s *request = response->request;
|
482
481
|
intptr_t bytes_sent = response->content_length;
|
483
482
|
|
@@ -485,9 +484,20 @@ static void http_response_log_finish(http_response_s *response) {
|
|
485
484
|
response->logged
|
486
485
|
? ((get_clock_mili() - response->clock_start) / CLOCK_RESOLUTION)
|
487
486
|
: 0;
|
488
|
-
|
487
|
+
|
488
|
+
/* pre-print log message every 1 or 2 seconds or so. */
|
489
|
+
static __thread time_t cached_tick;
|
490
|
+
static __thread char cached_httpdate[48];
|
491
|
+
static __thread size_t chached_len;
|
489
492
|
time_t last_tick = facil_last_tick();
|
490
|
-
|
493
|
+
if (last_tick > cached_tick) {
|
494
|
+
struct tm tm;
|
495
|
+
cached_tick = last_tick | 1;
|
496
|
+
http_gmtime(&last_tick, &tm);
|
497
|
+
chached_len = http_date2str(cached_httpdate, &tm);
|
498
|
+
fprintf(stderr, "Updated date cache to (%lu) %s\n", chached_len,
|
499
|
+
cached_httpdate);
|
500
|
+
}
|
491
501
|
|
492
502
|
// TODO Guess IP address from headers (forwarded) where possible
|
493
503
|
sock_peer_addr_s addrinfo = sock_peer_addr(response->fd);
|
@@ -509,7 +519,8 @@ static void http_response_log_finish(http_response_s *response) {
|
|
509
519
|
}
|
510
520
|
memcpy(buffer + pos, " - - [", 6);
|
511
521
|
pos += 6;
|
512
|
-
|
522
|
+
memcpy(buffer + pos, cached_httpdate, chached_len);
|
523
|
+
pos += chached_len;
|
513
524
|
buffer[pos++] = ']';
|
514
525
|
buffer[pos++] = ' ';
|
515
526
|
buffer[pos++] = '"';
|
data/ext/iodine/misc.c
CHANGED
@@ -54,16 +54,17 @@ fdump_s *bscrypt_fdump(const char *file_path, size_t size_limit) {
|
|
54
54
|
goto error;
|
55
55
|
if (size_limit == 0 || (size_t)f_data.st_size < size_limit)
|
56
56
|
size_limit = f_data.st_size;
|
57
|
-
container = malloc(size_limit + sizeof(fdump_s));
|
57
|
+
container = malloc(size_limit + sizeof(fdump_s) + 1);
|
58
58
|
if (!container)
|
59
59
|
goto error;
|
60
|
-
container->length = size_limit;
|
61
60
|
file = open(file_path, O_RDONLY);
|
62
61
|
if (file < 0)
|
63
62
|
goto error;
|
64
63
|
if (read(file, container->data, size_limit) != (ssize_t)size_limit)
|
65
64
|
goto error;
|
66
65
|
close(file);
|
66
|
+
container->length = size_limit;
|
67
|
+
container->data[size_limit] = 0;
|
67
68
|
return container;
|
68
69
|
error:
|
69
70
|
if (container)
|
data/ext/iodine/pubsub.c
CHANGED
data/ext/iodine/pubsub.h
CHANGED
data/ext/iodine/resp.h
CHANGED
@@ -126,7 +126,7 @@ void resp_free_object(resp_object_s *obj);
|
|
126
126
|
* to the size of the buffer.
|
127
127
|
*
|
128
128
|
* Once the function returns, `size` will be updated to include the number of
|
129
|
-
* bytes required for the string. If the function returned a
|
129
|
+
* bytes required for the string. If the function returned a failure, this value
|
130
130
|
* can be used to allocate enough memory to contain the string.
|
131
131
|
*
|
132
132
|
* The string is Binary safe and it ISN'T always NUL terminated.
|
@@ -134,8 +134,7 @@ void resp_free_object(resp_object_s *obj);
|
|
134
134
|
* The optional `parser` argument allows experimental extensions to be used when
|
135
135
|
* formatting the object. It can be ignored when formatting without extensions.
|
136
136
|
*
|
137
|
-
* **If implementing a server**:
|
138
|
-
* notifications.
|
137
|
+
* **If implementing a server**:
|
139
138
|
*
|
140
139
|
* When implementing a server, Pub/Sub should avoid multiple copies by using a
|
141
140
|
* dedicated buffer with a reference count. By decreasing the reference count
|
data/ext/iodine/sock.c
CHANGED
@@ -18,6 +18,8 @@ Includes and state
|
|
18
18
|
#include <fcntl.h>
|
19
19
|
#include <limits.h>
|
20
20
|
#include <netdb.h>
|
21
|
+
#include <netinet/in.h>
|
22
|
+
#include <netinet/tcp.h>
|
21
23
|
#include <stdio.h>
|
22
24
|
#include <string.h>
|
23
25
|
#include <sys/mman.h>
|
@@ -688,6 +690,7 @@ intptr_t sock_accept(intptr_t srv_uuid) {
|
|
688
690
|
struct sockaddr_in6 addrinfo;
|
689
691
|
socklen_t addrlen = sizeof(addrinfo);
|
690
692
|
int client;
|
693
|
+
int one = 1;
|
691
694
|
#ifdef SOCK_NONBLOCK
|
692
695
|
client = accept4(sock_uuid2fd(srv_uuid), (struct sockaddr *)&addrinfo,
|
693
696
|
&addrlen, SOCK_NONBLOCK);
|
@@ -700,10 +703,10 @@ intptr_t sock_accept(intptr_t srv_uuid) {
|
|
700
703
|
return -1;
|
701
704
|
sock_set_non_block(client);
|
702
705
|
#endif
|
706
|
+
setsockopt(client, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
703
707
|
clear_fd(client, 1);
|
704
708
|
fdinfo(client).addrinfo = addrinfo;
|
705
709
|
fdinfo(client).addrlen = addrlen;
|
706
|
-
// sock_touch(srv_uuid);
|
707
710
|
return fd2uuid(client);
|
708
711
|
}
|
709
712
|
|
@@ -733,6 +736,7 @@ before attempting to write to the socket.
|
|
733
736
|
*/
|
734
737
|
intptr_t sock_connect(char *address, char *port) {
|
735
738
|
int fd;
|
739
|
+
int one = 1;
|
736
740
|
// setup the address
|
737
741
|
struct addrinfo hints;
|
738
742
|
struct addrinfo *addrinfo; // will point to the results
|
@@ -763,6 +767,7 @@ intptr_t sock_connect(char *address, char *port) {
|
|
763
767
|
freeaddrinfo(addrinfo);
|
764
768
|
return -1;
|
765
769
|
}
|
770
|
+
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
766
771
|
clear_fd(fd, 1);
|
767
772
|
fdinfo(fd).addrinfo = *((struct sockaddr_in6 *)addrinfo->ai_addr);
|
768
773
|
fdinfo(fd).addrlen = addrinfo->ai_addrlen;
|
data/ext/iodine/sock.h
CHANGED
@@ -8,7 +8,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
8
8
|
*/
|
9
9
|
#define LIB_SOCK_VERSION_MAJOR 0
|
10
10
|
#define LIB_SOCK_VERSION_MINOR 3
|
11
|
-
#define LIB_SOCK_VERSION_PATCH
|
11
|
+
#define LIB_SOCK_VERSION_PATCH 2
|
12
12
|
|
13
13
|
/** \file
|
14
14
|
The `sock.h` is a non-blocking socket helper library, using a user level buffer,
|
@@ -16,8 +16,16 @@ non-blocking sockets and some helper functions.
|
|
16
16
|
|
17
17
|
This library is great when using it alongside `evio.h`.
|
18
18
|
|
19
|
-
The library is designed to be thread safe, but not fork safe
|
20
|
-
sockets, except listenning sockets, shouldn't be shared among processes
|
19
|
+
The library is designed to be thread safe, but not fork safe - mostly since
|
20
|
+
sockets, except listenning sockets, shouldn't be shared among processes.
|
21
|
+
|
22
|
+
Socket connections accepted or created using this library will use the
|
23
|
+
TCP_NODELAY option by default.
|
24
|
+
|
25
|
+
Non TCP/IP stream sockets and file descriptors (i.e., unix sockets) can be
|
26
|
+
safely used with this library. However, file descriptors that can't use the
|
27
|
+
`read` or `write` system calls MUST set correct Read / Write hooks or they will
|
28
|
+
fail.
|
21
29
|
*/
|
22
30
|
|
23
31
|
#include <stdint.h>
|
data/ext/iodine/websockets.c
CHANGED
@@ -881,6 +881,11 @@ void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id) {
|
|
881
881
|
The API implementation
|
882
882
|
*/
|
883
883
|
|
884
|
+
static void deferred_on_open(void *func, void *pr) {
|
885
|
+
((void (*)(ws_s *))func)(pr);
|
886
|
+
facil_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
887
|
+
}
|
888
|
+
|
884
889
|
/** The upgrade */
|
885
890
|
#undef websocket_upgrade
|
886
891
|
ssize_t websocket_upgrade(websocket_settings_s settings) {
|
@@ -973,6 +978,8 @@ refuse:
|
|
973
978
|
response->status = 400;
|
974
979
|
cleanup:
|
975
980
|
if (response->status == 101) {
|
981
|
+
// update the protocol object, cleanning up the old one
|
982
|
+
facil_attach_locked(ws->fd, (protocol_s *)ws);
|
976
983
|
// send the response
|
977
984
|
http_response_finish(response);
|
978
985
|
// we have an active websocket connection - prep the connection buffer
|
@@ -980,10 +987,9 @@ cleanup:
|
|
980
987
|
// update the timeout
|
981
988
|
facil_set_timeout(ws->fd, settings.timeout);
|
982
989
|
// call the on_open callback
|
983
|
-
if (settings.on_open)
|
984
|
-
settings.on_open
|
985
|
-
|
986
|
-
facil_attach(ws->fd, (protocol_s *)ws);
|
990
|
+
if (settings.on_open) {
|
991
|
+
defer(deferred_on_open, (void *)settings.on_open, ws);
|
992
|
+
}
|
987
993
|
return 0;
|
988
994
|
}
|
989
995
|
http_response_finish(response);
|
data/lib/iodine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iodine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- bin/ws-echo
|
121
121
|
- bin/ws-shootout
|
122
122
|
- examples/broadcast.ru
|
123
|
+
- examples/config.ru
|
123
124
|
- examples/echo.ru
|
124
125
|
- examples/hello.ru
|
125
126
|
- examples/redis.ru
|