iodine 0.4.3 → 0.4.4

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: 56640878e0c4cc4794f8cf3747a398a51847b0ae
4
- data.tar.gz: 0c58b292c6002ea03e068eba06f5aa75c449d236
3
+ metadata.gz: b38604622998dd86c23390d22f206cd77e235437
4
+ data.tar.gz: d82068cbc80b9ccd7aa0838efbc50e9fdfadc1f1
5
5
  SHA512:
6
- metadata.gz: 193156a5098163d096af9c182ed20b7a6977234575d4fd49a81ba07d1f44dc677f8772bd6c873f79f0466b28037290463d070473eb057e4c522127e0a51d5e8a
7
- data.tar.gz: ab58390cfd148cd7fa481cec9f3db47ccbe64188e993e9c34baaaab2b0eaaf51cb65c459d6a9ec0ee1315a0dcf0536d8931b72aae0d1fd2c0dd743216788aab0
6
+ metadata.gz: 15be0353bd34deb982497787ff2c79bff9a504410a364ba46a4fa23aebf66e1d28441b2cf6103773b02cedcb598a7c2e091d533eaff5b5acc6ce41cdfea7b695
7
+ data.tar.gz: 96652525a23cbd5fa0e88c69cd490502f857bb609fc3b40db073b1ccac3da3f25879213385a5d732df0b89a09c038c46f531034834cb6b1491c5ae4ac3340e3d
@@ -8,12 +8,15 @@ Please notice that this change log contains changes for upcoming releases as wel
8
8
 
9
9
  ***
10
10
 
11
- #### Change log v.0.4.3
11
+ #### Change log v.0.4.4
12
+
13
+ **Fix**: fixed an issue related to Ruby 2.3 optimizations of String management (an issue that didn't seem to effect Ruby 2.4). This fix disables the recyclable buffer implemented for the `on_message` Websocket callback. The callback will now receive a copy of the buffer (not the buffer itself), so there is no risk of collisions between the network buffer (managed in C) and the `on_message(data)` String (managed by Ruby).
12
14
 
13
- This release is a ghost hunt release, attempting to find an issue noticed only during when deploying in the Heroku production environment.
15
+ ***
14
16
 
15
- **Fix**: fixed a possible issue in fragmented pipelined Websocket messages. This
17
+ #### Change log v.0.4.3
16
18
 
19
+ **Fix**: fixed a possible issue in fragmented pipelined Websocket messages.
17
20
 
18
21
  ***
19
22
 
@@ -23,10 +23,12 @@ The Websocket Callback Object should be a class (or an instance of such class) w
23
23
 
24
24
  * `on_open()` WILL be called once the upgrade had completed.
25
25
 
26
- * `on_message(data)` WILL be called when incoming Websocket data is received. `data` will be a String with an encoding of UTF-8 for text messages and `binary` encoding for non-text messages (as specified by the Websocket Protocol).
26
+ * `on_message(data)` (REQUIRED) WILL be called when incoming Websocket data is received. `data` will be a String with an encoding of UTF-8 for text messages and `binary` encoding for non-text messages (as specified by the Websocket Protocol).
27
27
 
28
28
  The *client* **MUST** assume that the `data` String will be a **recyclable buffer** and that it's content will be corrupted the moment the `on_message` callback returns.
29
29
 
30
+ Servers MAY, optionally, implement a **recyclable buffer** for the `on_message` callback. However, this is optional and it is *not* required.
31
+
30
32
  * `on_ready()` **MAY** be called when the state of the out-going socket buffer changes from full to not full (data can be sent to the socket). **If** `has_pending?` returns `true`, the `on_ready` callback **MUST** be called once the buffer state changes.
31
33
 
32
34
  * `on_shutdown()` MAY be called during the server's graceful shutdown process, _before_ the connection is closed and in addition to the `on_close` function (which is called _after_ the connection is closed.
@@ -67,6 +69,8 @@ Connection `ping` / `pong`, timeouts and network considerations should be implem
67
69
 
68
70
  Server settings **MAY** (not required) be provided to allow for customization and adaptation for different network environments or websocket extensions. It is **RECOMMENDED** that any settings be available as command line arguments and **not** incorporated into the application's logic.
69
71
 
72
+ The requirement that the server extends the class of the Websocket Callback Object (instead of the client application doing so explicitly) is designed to allow the client application to be more server agnostic.
73
+
70
74
  ## Upgrading
71
75
 
72
76
  * **Server**: When an upgrade request is received, the server will set the `env['upgrade.websocket?']` flag to `true`, indicating that: 1. this specific request is upgradable; and 2. this server supports this specification.
@@ -111,12 +111,6 @@ static void http1_on_header_found(http_request_s *request,
111
111
  ((http1_request_s *)request)->header_pos += 1;
112
112
  }
113
113
 
114
- static void http1_on_data(intptr_t uuid, http1_protocol_s *pr);
115
- static void http1_on_data_def(intptr_t uuid, protocol_s *pr, void *ignr) {
116
- sock_touch(uuid);
117
- http1_on_data(uuid, (http1_protocol_s *)pr);
118
- (void)ignr;
119
- }
120
114
  /* parse and call callback */
121
115
  static void http1_on_data(intptr_t uuid, http1_protocol_s *pr) {
122
116
  ssize_t result;
@@ -230,8 +224,7 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *pr) {
230
224
  /* prevent this connection from hogging the thread by pipelining endless
231
225
  * requests.
232
226
  */
233
- facil_defer(.task = http1_on_data_def, .task_type = FIO_PR_LOCK_TASK,
234
- .uuid = uuid);
227
+ facil_force_event(uuid, FIO_EVENT_ON_DATA);
235
228
  return;
236
229
  }
237
230
  }
@@ -54,70 +54,70 @@ inline static ws_s *get_ws(VALUE obj) {
54
54
  Buffer management - Rubyfy the way the buffer is handled.
55
55
  ***************************************************************************** */
56
56
 
57
- struct buffer_s {
58
- void *data;
59
- size_t size;
60
- };
61
-
62
- /** returns a buffer_s struct, with a buffer (at least) `size` long. */
63
- struct buffer_s create_ws_buffer(ws_s *owner);
64
-
65
- /** returns a buffer_s struct, with a buffer (at least) `size` long. */
66
- struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s);
67
-
68
- /** releases an existing buffer. */
69
- void free_ws_buffer(ws_s *owner, struct buffer_s);
70
-
71
- /** Sets the initial buffer size. (4Kb)*/
72
- #define WS_INITIAL_BUFFER_SIZE 4096
73
-
74
- // buffer increments by 4,096 Bytes (4Kb)
75
- #define round_up_buffer_size(size) ((((size) >> 12) + 1) << 12)
76
-
77
- struct buffer_args {
78
- struct buffer_s buffer;
79
- ws_s *ws;
80
- };
81
-
82
- void *ruby_land_buffer(void *_buf) {
83
- #define args ((struct buffer_args *)(_buf))
84
- if (args->buffer.data == NULL) {
85
- VALUE rbbuff = rb_str_buf_new(WS_INITIAL_BUFFER_SIZE);
86
- rb_ivar_set(get_handler(args->ws), iodine_buff_var_id, rbbuff);
87
- rb_str_set_len(rbbuff, 0);
88
- rb_enc_associate(rbbuff, IodineBinaryEncoding);
89
- args->buffer.data = RSTRING_PTR(rbbuff);
90
- args->buffer.size = WS_INITIAL_BUFFER_SIZE;
91
-
92
- } else {
93
- VALUE rbbuff = rb_ivar_get(get_handler(args->ws), iodine_buff_var_id);
94
- rb_str_modify(rbbuff);
95
- rb_str_resize(rbbuff, args->buffer.size);
96
- args->buffer.data = RSTRING_PTR(rbbuff);
97
- args->buffer.size = rb_str_capacity(rbbuff);
98
- }
99
- return NULL;
100
- #undef args
101
- }
102
-
103
- struct buffer_s create_ws_buffer(ws_s *owner) {
104
- struct buffer_args args = {.ws = owner};
105
- RubyCaller.call_c(ruby_land_buffer, &args);
106
- return args.buffer;
107
- }
108
-
109
- struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s buffer) {
110
- buffer.size = round_up_buffer_size(buffer.size);
111
- struct buffer_args args = {.ws = owner, .buffer = buffer};
112
- RubyCaller.call_c(ruby_land_buffer, &args);
113
- return args.buffer;
114
- }
115
- void free_ws_buffer(ws_s *owner, struct buffer_s buff) {
116
- (void)(owner);
117
- (void)(buff);
118
- }
119
-
120
- #undef round_up_buffer_size
57
+ // struct buffer_s {
58
+ // void *data;
59
+ // size_t size;
60
+ // };
61
+ //
62
+ // /** returns a buffer_s struct, with a buffer (at least) `size` long. */
63
+ // struct buffer_s create_ws_buffer(ws_s *owner);
64
+ //
65
+ // /** returns a buffer_s struct, with a buffer (at least) `size` long. */
66
+ // struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s);
67
+ //
68
+ // /** releases an existing buffer. */
69
+ // void free_ws_buffer(ws_s *owner, struct buffer_s);
70
+ //
71
+ // /** Sets the initial buffer size. (4Kb)*/
72
+ // #define WS_INITIAL_BUFFER_SIZE 4096
73
+ //
74
+ // // buffer increments by 4,096 Bytes (4Kb)
75
+ // #define round_up_buffer_size(size) ((((size) >> 12) + 1) << 12)
76
+ //
77
+ // struct buffer_args {
78
+ // struct buffer_s buffer;
79
+ // ws_s *ws;
80
+ // };
81
+ //
82
+ // void *ruby_land_buffer(void *_buf) {
83
+ // #define args ((struct buffer_args *)(_buf))
84
+ // if (args->buffer.data == NULL) {
85
+ // VALUE rbbuff = rb_str_buf_new(WS_INITIAL_BUFFER_SIZE);
86
+ // rb_ivar_set(get_handler(args->ws), iodine_buff_var_id, rbbuff);
87
+ // rb_str_set_len(rbbuff, 0);
88
+ // rb_enc_associate(rbbuff, IodineBinaryEncoding);
89
+ // args->buffer.data = RSTRING_PTR(rbbuff);
90
+ // args->buffer.size = WS_INITIAL_BUFFER_SIZE;
91
+ //
92
+ // } else {
93
+ // VALUE rbbuff = rb_ivar_get(get_handler(args->ws), iodine_buff_var_id);
94
+ // rb_str_modify(rbbuff);
95
+ // rb_str_resize(rbbuff, args->buffer.size);
96
+ // args->buffer.data = RSTRING_PTR(rbbuff);
97
+ // args->buffer.size = rb_str_capacity(rbbuff);
98
+ // }
99
+ // return NULL;
100
+ // #undef args
101
+ // }
102
+ //
103
+ // struct buffer_s create_ws_buffer(ws_s *owner) {
104
+ // struct buffer_args args = {.ws = owner};
105
+ // RubyCaller.call_c(ruby_land_buffer, &args);
106
+ // return args.buffer;
107
+ // }
108
+ //
109
+ // struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s buffer) {
110
+ // buffer.size = round_up_buffer_size(buffer.size);
111
+ // struct buffer_args args = {.ws = owner, .buffer = buffer};
112
+ // RubyCaller.call_c(ruby_land_buffer, &args);
113
+ // return args.buffer;
114
+ // }
115
+ // void free_ws_buffer(ws_s *owner, struct buffer_s buff) {
116
+ // (void)(owner);
117
+ // (void)(buff);
118
+ // }
119
+ //
120
+ // #undef round_up_buffer_size
121
121
 
122
122
  /* *****************************************************************************
123
123
  Websocket Ruby API
@@ -641,7 +641,8 @@ void ws_on_open(ws_s *ws) {
641
641
  void ws_on_close(ws_s *ws) {
642
642
  VALUE handler = get_handler(ws);
643
643
  if (!handler) {
644
- fprintf(stderr, "Closing a handlerless websocket?!\n");
644
+ fprintf(stderr,
645
+ "ERROR: (iodine websockets) Closing a handlerless websocket?!\n");
645
646
  return;
646
647
  }
647
648
  RubyCaller.call(handler, iodine_on_close_func_id);
@@ -660,51 +661,43 @@ void ws_on_ready(ws_s *ws) {
660
661
  return;
661
662
  RubyCaller.call(handler, iodine_on_ready_func_id);
662
663
  }
663
- void ws_on_data(ws_s *ws, char *data, size_t length, uint8_t is_text) {
664
- (void)(data);
665
- VALUE handler = get_handler(ws);
666
- if (!handler)
667
- return;
668
- VALUE buffer = rb_ivar_get(handler, iodine_buff_var_id);
669
- if (is_text)
664
+
665
+ struct ws_on_data_args_s {
666
+ ws_s *ws;
667
+ void *data;
668
+ size_t length;
669
+ uint8_t is_text;
670
+ };
671
+ void *ws_on_data_inGIL(void *args_) {
672
+ struct ws_on_data_args_s *a = args_;
673
+ VALUE handler = get_handler(a->ws);
674
+ if (!handler) {
675
+ fprintf(stderr, "ERROR: iodine can't find Websocket handler!\n");
676
+ return NULL;
677
+ }
678
+ VALUE buffer = rb_str_new(a->data, a->length);
679
+ if (a->is_text)
670
680
  rb_enc_associate(buffer, IodineUTF8Encoding);
671
681
  else
672
682
  rb_enc_associate(buffer, IodineBinaryEncoding);
673
- rb_str_set_len(buffer, length);
674
- RubyCaller.call2(handler, iodine_on_message_func_id, 1, &buffer);
683
+ rb_funcallv(handler, iodine_on_message_func_id, 1, &buffer);
684
+ return NULL;
685
+ }
686
+ void ws_on_data(ws_s *ws, char *data, size_t length, uint8_t is_text) {
687
+ struct ws_on_data_args_s a = {
688
+ .ws = ws, .data = data, .length = length, .is_text = is_text};
689
+ RubyCaller.call_c(ws_on_data_inGIL, &a);
690
+ (void)(data);
675
691
  }
676
692
 
677
693
  //////////////
678
694
  // Empty callbacks for default implementations.
679
695
 
680
- /** Please implement your own callback for this event.
681
- */
696
+ /** Please implement your own callback for this event. */
682
697
  static VALUE empty_func(VALUE self) {
683
698
  (void)(self);
684
699
  return Qnil;
685
700
  }
686
- // /* The `on_message(data)` callback is the main method for any websocket
687
- // implementation. It is the only required callback for a websocket handler
688
- // (without this handler, errors will occur).
689
- //
690
- // <b>NOTICE</b>: the data passed to the `on_message` callback is the actual
691
- // recycble network buffer, not a copy! <b>Use `data.dup` before moving the data
692
- // out of the function's scope</b> to prevent data corruption (i.e. when
693
- // using the data within an `each` block). For example (broadcasting):
694
- //
695
- // def on_message data
696
- // msg = data.dup; # data will be overwritten once the function exists.
697
- // each {|ws| ws.write msg}
698
- // end
699
- //
700
- // Please override this method and implement your own callback.
701
- // */
702
- // static VALUE def_dyn_message(VALUE self, VALUE data) {
703
- // fprintf(stderr,
704
- // "WARNING: websocket handler on_message override missing or "
705
- // "bypassed.\n");
706
- // return Qnil;
707
- // }
708
701
 
709
702
  /* *****************************************************************************
710
703
  Upgrading
@@ -771,7 +764,7 @@ void Iodine_init_websocket(void) {
771
764
  fprintf(stderr, "WTF?!\n"), exit(-1);
772
765
  // // callbacks and handlers
773
766
  rb_define_method(IodineWebsocket, "on_open", empty_func, 0);
774
- // rb_define_method(IodineWebsocket, "on_message", def_dyn_message, 1);
767
+ // rb_define_method(IodineWebsocket, "on_message", empty_func_message, 1);
775
768
  rb_define_method(IodineWebsocket, "on_shutdown", empty_func, 0);
776
769
  rb_define_method(IodineWebsocket, "on_close", empty_func, 0);
777
770
  rb_define_method(IodineWebsocket, "on_ready", empty_func, 0);
@@ -862,9 +862,10 @@ ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
862
862
  return rw->read(uuid, buf, count);
863
863
  int old_errno = errno;
864
864
  ssize_t ret = rw->read(uuid, buf, count);
865
- sock_touch(uuid);
866
- if (ret > 0)
865
+ if (ret > 0) {
866
+ sock_touch(uuid);
867
867
  return ret;
868
+ }
868
869
  if (ret < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR ||
869
870
  errno == ENOTCONN)) {
870
871
  errno = old_errno;
@@ -115,8 +115,6 @@ struct Websocket {
115
115
  struct buffer_s buffer;
116
116
  /** message length (how much of the buffer actually used). */
117
117
  size_t length;
118
- /** for fragmenting the `on_data` parsing and message handling. */
119
- size_t resume_from;
120
118
  /** parser. */
121
119
  struct {
122
120
  union {
@@ -233,12 +231,6 @@ static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
233
231
  static size_t websocket_encode(void *buff, void *data, size_t len, char text,
234
232
  char first, char last, char client);
235
233
 
236
- static void on_data(intptr_t sockfd, protocol_s *_ws);
237
- static void on_data_def(intptr_t sockfd, protocol_s *_ws, void *arg) {
238
- on_data(sockfd, _ws);
239
- (void)arg;
240
- }
241
-
242
234
  /* read data from the socket, parse it and invoke the websocket events. */
243
235
  static void on_data(intptr_t sockfd, protocol_s *_ws) {
244
236
  #define ws ((ws_s *)_ws)
@@ -246,10 +238,11 @@ static void on_data(intptr_t sockfd, protocol_s *_ws) {
246
238
  return;
247
239
  ssize_t len = 0;
248
240
  ssize_t data_len = 0;
241
+ read_buffer.pos = 0;
242
+
249
243
  if ((len = sock_read(sockfd, read_buffer.buffer, WEBSOCKET_READ_MAX)) <= 0)
250
244
  return;
251
- data_len = 0;
252
- read_buffer.pos = 0;
245
+
253
246
  while (read_buffer.pos < len) {
254
247
  // collect the frame's head
255
248
  if (!ws->parser.state.has_head) {
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.4.3'.freeze
2
+ VERSION = '0.4.4'.freeze
3
3
  end
@@ -7,6 +7,8 @@ module Iodine
7
7
  # * Server <=> Client relations ({Iodine::Websocket#write}, {Iodine::Websocket#close} etc')
8
8
  # * Client <=> Server <=> Client relations ({Iodine::Websocket#subscribe}, {Iodine::Websocket#publish}, etc').
9
9
  # * Task scheduling ({Iodine::Websocket.defer}, {Iodine::Websocket#defer}, {Iodine::Websocket.each}).
10
+ #
11
+ # Notice that Websocket callback objects (as specified by the {file:SPEC-Websocket-Draft.md proposed Rack specification} *MUST* provide an `on_message(data)` callback.
10
12
  module Websocket
11
13
  end
12
14
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev