iodine 0.5.1 → 0.5.2

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
  SHA256:
3
- metadata.gz: '0801b0def77514bb565e4330fba285d1a35fc84fb039e8931791f3f49af164f7'
4
- data.tar.gz: 2f17d26fbae2495c70f1b4d9f822641ad8501940c872a4cd3e1d7636bbfdb1a9
3
+ metadata.gz: 360dcbeabc106918078a92d3902fc8ef1a94323c933e028c99224a2ef3b5409a
4
+ data.tar.gz: 75425229c8fefd9429d0f8d0936186d13668eb7b3f7c8905e16e3d617cdd3390
5
5
  SHA512:
6
- metadata.gz: abbb75f0b612e43054d2c1e0725a8bcb7c70815be580fe1b62d6f64ae203a30ac41508f8652b9a005b8d65de17bf0e9f404b9b73a5b6899e2115dfca51bc9d1a
7
- data.tar.gz: a1cc7e4f532b93c33e3cdb29040eef2ce02b2c3d6b03d13fb3daa96bbce3bc986dae0132694feb41cbc7b0c5c8d689cfe393b0b44cbce1a93ede3745f6d87219
6
+ metadata.gz: 1f437aad47df8fc727bf360692597e31ad41e297d1ff7dfe46786b9d3d7a9c40ff334e3eeca8cec1927b2143d4509b2ce509eaf192d9d521f4113b0b68a1cec8
7
+ data.tar.gz: ca852958d4f264f907911f95fbd44288b8e6b525afdfc5708b51d2ea13b3d8e236c166544b19686151fdf2c73741b7353e2c2a90b28702275158f16bdf311eeb
@@ -6,7 +6,11 @@ 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.5.1 (next release)
9
+ #### Change log v.0.5.2
10
+
11
+ **Fix**: fixed compilation issues on FreeBSD. Credit to @adam12 (Adam Daniels) for opening issue #35 and offering a patch.
12
+
13
+ #### Change log v.0.5.1
10
14
 
11
15
  **Fix**: fixed compilation issues on OS X version < 10.12 and Alpine Linux. Credit to @jdickey (Jeff Dickey) for opening issue #32.
12
16
 
data/README.md CHANGED
@@ -27,6 +27,22 @@ Iodine is an **evented** framework with a simple API that builds off the low lev
27
27
 
28
28
  Iodine is a C extension for Ruby, developed and optimized for Ruby MRI 2.2.2 and up... it should support the whole Ruby 2.0 MRI family, but Rack requires Ruby 2.2.2, and so iodine matches this requirement.
29
29
 
30
+ ---
31
+
32
+ ## Important Note about API Changes and Standardization
33
+
34
+ I am very thankful to the many early adopters of iodine using the WebSocket and Pub/Sub API.
35
+
36
+ Please note that Iodine 0.5.0's API is a temporary API that was part of an attempt to come up with a de facto Rack standard for WebSocket / SSE connectivity.
37
+
38
+ Sadly, this standard isn't progressing as well as I had hoped. More API changes were requested and it seems that the PR with the finalized API is still being considered ([please vote for the PR!](https://github.com/rack/rack/pull/1272)).
39
+
40
+ I'm working on Iodine 0.6.0 with the requested updates to the API, along with an updated Pub/Sub API that should fit better with existing pub/sub approaches such ActionCable.
41
+
42
+ It's my sincere hope that the upcoming API changes in 0.6.0 will be the last and that this will signal the API's maturity as well as general acceptance.
43
+
44
+ ---
45
+
30
46
  ## Iodine::Rack == fast & powerful HTTP + Websockets server with native Pub/Sub
31
47
 
32
48
  Iodine includes a light and fast HTTP and Websocket server written in C that was written according to the [Rack interface specifications](http://www.rubydoc.info/github/rack/rack/master/file/SPEC) and the [Websocket draft extension](./SPEC-Websocket-Draft.md).
@@ -12,6 +12,8 @@ Feel free to copy, use and enjoy according to the license provided.
12
12
 
13
13
  #ifdef EVIO_ENGINE_KQUEUE
14
14
 
15
+ #include <sys/types.h>
16
+
15
17
  #include <assert.h>
16
18
  #include <errno.h>
17
19
  #include <fcntl.h>
@@ -20,13 +22,12 @@ Feel free to copy, use and enjoy according to the license provided.
20
22
  #include <stdio.h>
21
23
  #include <stdlib.h>
22
24
  #include <string.h>
23
- #include <sys/event.h>
24
25
  #include <sys/socket.h>
25
- #include <sys/time.h>
26
- #include <sys/types.h>
27
26
  #include <time.h>
28
27
  #include <unistd.h>
29
28
 
29
+ #include <sys/event.h>
30
+ #include <sys/time.h>
30
31
  /* *****************************************************************************
31
32
  Global data and system independant code
32
33
  ***************************************************************************** */
@@ -1697,7 +1697,9 @@ static void facil_worker_cleanup(void) {
1697
1697
  }
1698
1698
  }
1699
1699
  defer_perform();
1700
- facil_data->on_finish();
1700
+ if (facil_data->on_finish) {
1701
+ facil_data->on_finish();
1702
+ }
1701
1703
  defer_perform();
1702
1704
  evio_close();
1703
1705
  facil_external_cleanup();
@@ -1901,56 +1903,77 @@ static inline size_t facil_detect_cpu_cores(void) {
1901
1903
  return cpu_count;
1902
1904
  }
1903
1905
 
1904
- #undef facil_run
1905
- void facil_run(struct facil_run_args args) {
1906
- signal(SIGPIPE, SIG_IGN);
1907
- if (!facil_data)
1908
- facil_lib_init();
1909
- if (!args.on_idle)
1910
- args.on_idle = mock_idle;
1911
- if (!args.on_finish)
1912
- args.on_finish = mock_idle;
1913
- if (!args.threads && !args.processes) {
1906
+ /**
1907
+ * Returns the number of expected threads / processes to be used by facil.io.
1908
+ *
1909
+ * The pointers should start with valid values that match the expected threads /
1910
+ * processes values passed to `facil_run`.
1911
+ *
1912
+ * The data in the pointers will be overwritten with the result.
1913
+ */
1914
+ void facil_expected_concurrency(int16_t *threads, int16_t *processes) {
1915
+ if (!threads || !processes)
1916
+ return;
1917
+ if (!*threads && !*processes) {
1914
1918
  /* both options set to 0 - default to cores*cores matrix */
1915
1919
  ssize_t cpu_count = facil_detect_cpu_cores();
1916
1920
  #if FACIL_CPU_CORES_LIMIT
1917
1921
  if (cpu_count > FACIL_CPU_CORES_LIMIT) {
1918
- fprintf(
1919
- stderr,
1920
- "INFO: Detected %zu cores. Capping auto-detection of cores "
1921
- "to %zu.\n"
1922
- " Avoid this message by setting threads / workers manually.\n"
1923
- " To increase auto-detection limit, recompile with:\n"
1924
- " -DFACIL_CPU_CORES_LIMIT=%zu \n",
1925
- (size_t)cpu_count, (size_t)FACIL_CPU_CORES_LIMIT, (size_t)cpu_count);
1922
+ static int print_cores_warning = 1;
1923
+ if (print_cores_warning) {
1924
+ fprintf(
1925
+ stderr,
1926
+ "INFO: Detected %zu cores. Capping auto-detection of cores "
1927
+ "to %zu.\n"
1928
+ " Avoid this message by setting threads / workers manually.\n"
1929
+ " To increase auto-detection limit, recompile with:\n"
1930
+ " -DFACIL_CPU_CORES_LIMIT=%zu \n",
1931
+ (size_t)cpu_count, (size_t)FACIL_CPU_CORES_LIMIT,
1932
+ (size_t)cpu_count);
1933
+ print_cores_warning = 0;
1934
+ }
1926
1935
  cpu_count = FACIL_CPU_CORES_LIMIT;
1927
1936
  }
1928
1937
  #endif
1929
- args.threads = args.processes = (int16_t)cpu_count;
1930
- } else if (args.threads < 0 || args.processes < 0) {
1938
+ *threads = *processes = (int16_t)cpu_count;
1939
+ } else if (*threads < 0 || *processes < 0) {
1931
1940
  /* Set any option that is less than 0 be equal to cores/value */
1932
1941
  /* Set any option equal to 0 be equal to the other option in value */
1933
1942
  ssize_t cpu_count = facil_detect_cpu_cores();
1934
1943
 
1935
1944
  if (cpu_count > 0) {
1936
- int16_t threads = 0;
1937
- if (args.threads < 0)
1938
- threads = (int16_t)(cpu_count / (args.threads * -1));
1939
- else if (args.threads == 0)
1940
- threads = -1 * args.processes;
1941
- if (args.processes < 0)
1942
- args.processes = (int16_t)(cpu_count / (args.processes * -1));
1943
- else if (args.processes == 0)
1944
- args.processes = -1 * args.threads;
1945
- args.threads = threads;
1945
+ int16_t tmp_threads = 0;
1946
+ if (*threads < 0)
1947
+ tmp_threads = (int16_t)(cpu_count / (*threads * -1));
1948
+ else if (*threads == 0)
1949
+ tmp_threads = -1 * *processes;
1950
+ if (*processes < 0)
1951
+ *processes = (int16_t)(cpu_count / (*processes * -1));
1952
+ else if (*processes == 0)
1953
+ *processes = -1 * *threads;
1954
+ *threads = tmp_threads;
1946
1955
  }
1947
1956
  }
1948
1957
 
1949
1958
  /* make sure we have at least one process and at least one thread */
1950
- if (args.processes <= 0)
1951
- args.processes = 1;
1952
- if (args.threads <= 0)
1953
- args.threads = 1;
1959
+ if (*processes <= 0)
1960
+ *processes = 1;
1961
+ if (*threads <= 0)
1962
+ *threads = 1;
1963
+ }
1964
+
1965
+ #undef facil_run
1966
+ void facil_run(struct facil_run_args args) {
1967
+ signal(SIGPIPE, SIG_IGN);
1968
+ if (!facil_data)
1969
+ facil_lib_init();
1970
+ if (!args.on_idle)
1971
+ args.on_idle = mock_idle;
1972
+ if (!args.on_finish)
1973
+ args.on_finish = mock_idle;
1974
+
1975
+ /* compute actual concurrency */
1976
+ facil_expected_concurrency(&args.threads, &args.processes);
1954
1977
 
1955
1978
  /* listen to SIGINT / SIGTERM */
1956
1979
  facil_setup_signal_handler();
@@ -121,7 +121,7 @@ struct FacilIOProtocol {
121
121
  * Called when the server is shutting down, immediately before closing the
122
122
  * connection.
123
123
  *
124
- * The callback runs within a {FIO_PR_LOCK_TACK} lock, so it will never run
124
+ * The callback runs within a {FIO_PR_LOCK_TASK} lock, so it will never run
125
125
  * concurrently wil {on_data} or other connection specific tasks.
126
126
  */
127
127
  void (*on_shutdown)(intptr_t uuid, protocol_s *protocol);
@@ -336,6 +336,7 @@ struct facil_run_args {
336
336
  /** called when the server is done, to clean up any leftovers. */
337
337
  void (*on_finish)(void);
338
338
  };
339
+
339
340
  /**
340
341
  * Starts the facil.io event loop. This function will return after facil.io is
341
342
  * done (after shutdown).
@@ -345,6 +346,16 @@ struct facil_run_args {
345
346
  void facil_run(struct facil_run_args args);
346
347
  #define facil_run(...) facil_run((struct facil_run_args){__VA_ARGS__})
347
348
 
349
+ /**
350
+ * Returns the number of expected threads / processes to be used by facil.io.
351
+ *
352
+ * The pointers should start with valid values that match the expected threads /
353
+ * processes values passed to `facil_run`.
354
+ *
355
+ * The data in the pointers will be overwritten with the result.
356
+ */
357
+ void facil_expected_concurrency(int16_t *threads, int16_t *processes);
358
+
348
359
  /**
349
360
  * returns true (1) if the facil.io engine is already running.
350
361
  */
@@ -140,9 +140,10 @@ static inline void *sys_alloc(size_t len, uint8_t is_indi) {
140
140
  static void *next_alloc = NULL;
141
141
  /* hope for the best? */
142
142
  #ifdef MAP_ALIGNED
143
- result = mmap(
144
- next_alloc, len, PROT_READ | PROT_WRITE,
145
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED(FIO_MEMORY_BLOCK_SIZE), -1, 0);
143
+ result =
144
+ mmap(next_alloc, len, PROT_READ | PROT_WRITE,
145
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED(FIO_MEMORY_BLOCK_SIZE_LOG),
146
+ -1, 0);
146
147
  #else
147
148
  result = mmap(next_alloc, len, PROT_READ | PROT_WRITE,
148
149
  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -122,8 +122,11 @@ void fio_malloc_test(void);
122
122
  #endif
123
123
 
124
124
  /** Allocator default settings. */
125
+ #ifndef FIO_MEMORY_BLOCK_SIZE_LOG
126
+ #define FIO_MEMORY_BLOCK_SIZE_LOG (17) /* 17 == 128Kb */
127
+ #endif
125
128
  #ifndef FIO_MEMORY_BLOCK_SIZE
126
- #define FIO_MEMORY_BLOCK_SIZE ((uintptr_t)1 << 17) /* 17 == 128Kb */
129
+ #define FIO_MEMORY_BLOCK_SIZE ((uintptr_t)1 << FIO_MEMORY_BLOCK_SIZE_LOG)
127
130
  #endif
128
131
  #ifndef FIO_MEMORY_BLOCK_MASK
129
132
  #define FIO_MEMORY_BLOCK_MASK (FIO_MEMORY_BLOCK_SIZE - 1) /* 0b111... */
@@ -2289,6 +2289,7 @@ size_t http_date2rfc2109(char *target, struct tm *tmbuf) {
2289
2289
  pos[0] = MONTH_NAMES[tmbuf->tm_mon][0];
2290
2290
  pos[1] = MONTH_NAMES[tmbuf->tm_mon][1];
2291
2291
  pos[2] = MONTH_NAMES[tmbuf->tm_mon][2];
2292
+ pos[3] = ' ';
2292
2293
  pos += 4;
2293
2294
  // write year.
2294
2295
  pos += fio_ltoa(pos, tmbuf->tm_year + 1900, 10);
@@ -449,14 +449,17 @@ sock_peer_addr_s http_peer_addr(http_s *h);
449
449
  * It's possible to hijack the socket and than reconnect it to a new protocol
450
450
  * object.
451
451
  *
452
- * If any HTTP functions are called after the hijacking, the protocol object
453
- * might attempt to continue reading data from the buffer.
452
+ * It's possible to call `http_finish` immediately after calling `http_hijack`
453
+ * in order to send the outgoing headers.
454
+ *
455
+ * If any aditional HTTP functions are called after the hijacking, the protocol
456
+ * object might attempt to continue reading data from the buffer.
454
457
  *
455
458
  * Returns the underlining socket connection's uuid. If `leftover` isn't NULL,
456
459
  * it will be populated with any remaining data in the HTTP buffer (the data
457
- * will be automatically deallocated, so copy the data if it's required).
460
+ * will be automatically deallocated, so copy the data when in need).
458
461
  *
459
- * WARNING: this isn't a good was to handle HTTP connections, especially as
462
+ * WARNING: this isn't a good way to handle HTTP connections, especially as
460
463
  * HTTP/2 enters the picture.
461
464
  */
462
465
  intptr_t http_hijack(http_s *h, fio_cstr_s *leftover);
@@ -509,7 +509,7 @@ static int http1_upgrade2sse(http_s *h, http_sse_s *sse) {
509
509
 
510
510
  if (facil_attach(uuid, &sse_pr->p))
511
511
  return -1;
512
-
512
+ facil_set_timeout(uuid, handle2pr(h)->p.settings->ws_timeout);
513
513
  if (sse->on_open)
514
514
  sse->on_open(&sse_pr->sse->sse);
515
515
 
@@ -332,6 +332,15 @@ pubsub_sub_pt pubsub_find_sub(struct pubsub_subscribe_args args) {
332
332
  #define pubsub_find_sub(...) \
333
333
  pubsub_find_sub((struct pubsub_subscribe_args){__VA_ARGS__})
334
334
 
335
+ /**
336
+ * This helper returns a temporary handle to an existing subscription's channel.
337
+ *
338
+ * To keep the handle beyond the lifetime of the subscription, use `fiobj_dup`.
339
+ */
340
+ FIOBJ pubsub_sub_channel(pubsub_sub_pt sub) {
341
+ return (((channel_s *)((client_s *)sub)->parent))->name;
342
+ }
343
+
335
344
  /**
336
345
  * Unsubscribes from a specific subscription.
337
346
  *
@@ -406,11 +415,24 @@ static void pubsub_on_channel_destroy(channel_s *ch) {
406
415
 
407
416
  /** Registers an engine, so it's callback can be called. */
408
417
  void pubsub_engine_register(pubsub_engine_s *engine) {
418
+ if (!engine) {
419
+ return;
420
+ }
409
421
  spn_lock(&engn_lock);
410
422
  fio_hash_insert(
411
423
  &engines,
412
424
  (fio_hash_key_s){.hash = (uintptr_t)engine, .obj = FIOBJ_INVALID},
413
425
  engine);
426
+ if (engine->subscribe) {
427
+ FIO_HASH_FOR_LOOP(&channels, i) {
428
+ channel_s *ch = i->obj;
429
+ engine->subscribe(engine, ch->name, 0);
430
+ }
431
+ FIO_HASH_FOR_LOOP(&patterns, i) {
432
+ channel_s *ch = i->obj;
433
+ engine->subscribe(engine, ch->name, 1);
434
+ }
435
+ }
414
436
  spn_unlock(&engn_lock);
415
437
  }
416
438
 
@@ -108,6 +108,13 @@ pubsub_sub_pt pubsub_find_sub(struct pubsub_subscribe_args);
108
108
  #define pubsub_find_sub(...) \
109
109
  pubsub_find_sub((struct pubsub_subscribe_args){__VA_ARGS__})
110
110
 
111
+ /**
112
+ * This helper returns a temporary handle to an existing subscription's channel.
113
+ *
114
+ * To keep the handle beyond the lifetime of the subscription, use `fiobj_dup`.
115
+ */
116
+ FIOBJ pubsub_sub_channel(pubsub_sub_pt);
117
+
111
118
  /**
112
119
  * Unsubscribes from a specific subscription.
113
120
  *
@@ -1090,7 +1090,7 @@ ssize_t sock_write2_fn(sock_write_info_s options) {
1090
1090
  if (validate_uuid(options.uuid) || !options.buffer)
1091
1091
  goto error;
1092
1092
  lock_fd(fd);
1093
- if (!fdinfo(fd).open) {
1093
+ if (!fdinfo(fd).open || fdinfo(fd).close) {
1094
1094
  unlock_fd(fd);
1095
1095
  goto error;
1096
1096
  }
@@ -39,6 +39,11 @@ Feel free to copy, use and enjoy according to the license provided.
39
39
  #include <sys/types.h>
40
40
  #include <unistd.h>
41
41
 
42
+ #if defined(__FreeBSD__)
43
+ #include <netinet/in.h>
44
+ #include <sys/socket.h>
45
+ #endif
46
+
42
47
  // clang-format off
43
48
  #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
44
49
  # if defined(__has_include)
@@ -81,10 +81,10 @@ static inline int spn_trylock(spn_lock_i *lock) {
81
81
  }
82
82
 
83
83
  /** Releases a lock. */
84
- static inline __attribute__((unused)) void spn_unlock(spn_lock_i *lock) {
85
- __asm__ volatile("" ::: "memory");
86
- *lock = 0;
84
+ static inline __attribute__((unused)) int spn_unlock(spn_lock_i *lock) {
85
+ return SPN_LOCK_BUILTIN(lock, 0);
87
86
  }
87
+
88
88
  /** returns a lock's state (non 0 == Busy). */
89
89
  static inline __attribute__((unused)) int spn_is_locked(spn_lock_i *lock) {
90
90
  __asm__ volatile("" ::: "memory");
@@ -9,6 +9,7 @@ Feel free to copy, use and enjoy according to the license provided.
9
9
  #include "fio_llist.h"
10
10
  #include "fiobj.h"
11
11
 
12
+ #include "evio.h"
12
13
  #include "fio_base64.h"
13
14
  #include "fio_sha1.h"
14
15
  #include "http.h"
@@ -286,10 +287,13 @@ static void on_data_first(intptr_t sockfd, protocol_s *ws_) {
286
287
  if (ws->on_open)
287
288
  ws->on_open(ws);
288
289
  ws->protocol.on_data = on_data;
290
+ ws->protocol.on_ready = on_ready;
289
291
 
290
- if (ws->length)
292
+ if (ws->length) {
291
293
  ws->length = websocket_consume(ws->buffer.data, ws->length, ws,
292
294
  (~(ws->is_client) & 1));
295
+ }
296
+ evio_add_write(sock_uuid2fd(sockfd), (void *)sockfd);
293
297
 
294
298
  facil_force_event(sockfd, FIO_EVENT_ON_DATA);
295
299
  }
@@ -310,7 +314,7 @@ static ws_s *new_websocket(intptr_t uuid) {
310
314
  .protocol.ping = ws_ping,
311
315
  .protocol.on_data = on_data_first,
312
316
  .protocol.on_close = on_close,
313
- .protocol.on_ready = on_ready,
317
+ .protocol.on_ready = NULL /* filled in after `on_open` */,
314
318
  .protocol.on_shutdown = on_shutdown,
315
319
  .subscriptions = FIO_LS_INIT(ws->subscriptions),
316
320
  .is_client = 0,
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.5.1'.freeze
2
+ VERSION = '0.5.2'.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.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-02 00:00:00.000000000 Z
11
+ date: 2018-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack