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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9f26da78f47f0d2d44534b5a0930862ac5c1092
4
- data.tar.gz: 13b1d9adfa9e1df5f4544448ec551f879c6a32ac
3
+ metadata.gz: 481aeb644c0f6d2b38f2c00bc98412cc867e77d5
4
+ data.tar.gz: bd3eebc92151a3f2d4f41456c6d9402e7cc005fb
5
5
  SHA512:
6
- metadata.gz: '0381cd3d98e09b440636e7d4fd53a60139d7673b9595a2b7c9e2c6050d593f4920294496b462cf8661cd7cb3af464077664a78d6ba7afa27143b73bc16087678'
7
- data.tar.gz: bb0f81ea6a19da939cf733a3a934c7d2396b9bc980abdff4331fb0c2e03c65a96918341df0ff41fb50e5379de758fa75e4df31e0f8db15b3d3b41bd38e5fcdb6
6
+ metadata.gz: 4f31a4c09c7cbfe4df0a9f1df10c9c12004c7e92e3a2cbe1b513afff12c4c5670a457e0f4f6e8cf710ae5cb677d6b3c319f30baac802aefac054f8c8ccda058b
7
+ data.tar.gz: 9332123f1197af323c1b1af3a5fb1c5bbce7619b6586c1fb6ca049d220d9ebe6780637f5212588b55552e1bf17a07823cf5aedd1062dbf072095b1418df71cd5
@@ -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 libraries](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.
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
 
@@ -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
@@ -379,7 +379,6 @@ int defer_perform_in_fork(unsigned int process_count,
379
379
  }
380
380
  }
381
381
 
382
- pids_count++;
383
382
  forked_pool = &pool_placeholder;
384
383
  forked_pool = defer_pool_start(thread_count);
385
384
 
@@ -17,7 +17,7 @@ forked process.
17
17
  #define H_DEFER_H
18
18
  #define LIB_DEFER_VERSION_MAJOR 0
19
19
  #define LIB_DEFER_VERSION_MINOR 1
20
- #define LIB_DEFER_VERSION_PATCH 0
20
+ #define LIB_DEFER_VERSION_PATCH 2
21
21
 
22
22
  #ifdef __cplusplus
23
23
  extern "C" {
@@ -24,7 +24,7 @@ The callbacks supported by thils library:
24
24
  #define H_FACIL_EVIO_H
25
25
  #define LIB_EVIO_VERSION_MAJOR 0
26
26
  #define LIB_EVIO_VERSION_MINOR 1
27
- #define LIB_EVIO_VERSION_PATCH 0
27
+ #define LIB_EVIO_VERSION_PATCH 2
28
28
 
29
29
  #ifdef __cplusplus
30
30
  extern "C" {
@@ -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
- intptr_t uuid = connector->uuid = sock_connect(opt.address, opt.port);
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
- /** Attaches (or updates) a protocol object to a socket UUID.
1084
- * Returns -1 on error and 0 on success.
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->rsv = 0;
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)) {
@@ -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
 
@@ -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
- #define LIB_FACIL_HTTP_VERSION_MAJOR 0
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. */
@@ -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
- for (;;) {
121
- // handle requests with no file data
122
- if (request->request.body_file <= 0) {
123
- // request headers parsing
124
- if (pr->len == 0) {
125
- buffer = request->buffer;
126
- // make sure headers don't overflow
127
- pr->len = sock_read(uuid, buffer + request->buffer_pos,
128
- HTTP1_MAX_HEADER_SIZE - request->buffer_pos);
129
- // update buffer read position.
130
- request->buffer_pos += pr->len;
131
- // if (len > 0) {
132
- // fprintf(stderr, "\n----\nRead from socket, %lu bytes, total
133
- // %lu:\n",
134
- // len, request->buffer_pos);
135
- // for (size_t i = 0; i < request->buffer_pos; i++) {
136
- // fprintf(stderr, "%c", buffer[i] ? buffer[i] : '-');
137
- // }
138
- // fprintf(stderr, "\n");
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 (request->request.body_file > 0) {
173
- // fprintf(stderr, "Body File\n");
174
- parse_body:
175
- buffer = buff;
176
- // request body parsing
177
- pr->len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
178
- if (pr->len <= 0)
179
- goto finished_reading;
180
- result = http1_parse_request_body(buffer, pr->len, &request->request);
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
- continue;
190
- handle_request:
191
- // review required headers / data
192
- if (request->request.host == NULL)
193
- goto bad_request;
194
- http_settings_s *settings = pr->settings;
195
- request->request.settings = settings;
196
- // call request callback
197
- if (pr && settings &&
198
- (request->request.upgrade || settings->public_folder == NULL ||
199
- http_response_sendfile2(
200
- NULL, &request->request, settings->public_folder,
201
- settings->public_folder_length, request->request.path,
202
- request->request.path_len, settings->log_static))) {
203
- pr->on_request(&request->request);
204
- // fprintf(stderr, "Called on_request\n");
205
- }
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
- if (pr->len) {
224
- /* prevent this connection from hogging the thread by pipelining endless
225
- * requests.
226
- */
227
- facil_force_event(uuid, FIO_EVENT_ON_DATA);
228
- return;
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
- // no routes lead here.
232
- fprintf(stderr,
233
- "I am lost on a deserted island, no code can reach me here :-)\n");
234
- goto finished_reading; // How did we get here?
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 `next` field to store parser state.
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 `next` field data with the received content length
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.next += to_read;
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) {
@@ -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, intended for request pooling / chaining */
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;
@@ -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
- struct tm tm;
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
- http_gmtime(&last_tick, &tm);
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
- pos += http_date2str(buffer + pos, &tm);
522
+ memcpy(buffer + pos, cached_httpdate, chached_len);
523
+ pos += chached_len;
513
524
  buffer[pos++] = ']';
514
525
  buffer[pos++] = ' ';
515
526
  buffer[pos++] = '"';
@@ -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)
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright: Boaz segev, 2016-2017
2
+ Copyright: Boaz segev, 2017
3
3
  License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright: Boaz segev, 2016-2017
2
+ Copyright: Boaz segev, 2017
3
3
  License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
@@ -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 failuer, this value
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**: DON'T use this to format outgoing pub/sub
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
@@ -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;
@@ -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 0
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 (mostly since
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>
@@ -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(ws);
985
- // update the protocol object, cleanning up the old one
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);
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.4.6'.freeze
2
+ VERSION = '0.4.7'.freeze
3
3
  end
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.6
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-05 00:00:00.000000000 Z
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