iodine 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LIMITS.md +25 -0
  4. data/README.md +39 -80
  5. data/SPEC-Websocket-Draft.md +129 -4
  6. data/bin/echo +2 -2
  7. data/bin/http-hello +1 -0
  8. data/bin/updated api +113 -0
  9. data/bin/ws-echo +0 -1
  10. data/examples/broadcast.ru +56 -0
  11. data/examples/echo.ru +57 -0
  12. data/examples/hello.ru +30 -0
  13. data/examples/redis.ru +69 -0
  14. data/examples/shootout.ru +53 -0
  15. data/exe/iodine +2 -80
  16. data/ext/iodine/defer.c +11 -5
  17. data/ext/iodine/empty.h +26 -0
  18. data/ext/iodine/evio.h +1 -1
  19. data/ext/iodine/facil.c +103 -61
  20. data/ext/iodine/facil.h +20 -12
  21. data/ext/iodine/fio_dict.c +446 -0
  22. data/ext/iodine/fio_dict.h +90 -0
  23. data/ext/iodine/fio_hash_table.h +370 -0
  24. data/ext/iodine/fio_list.h +30 -3
  25. data/ext/iodine/http.c +169 -37
  26. data/ext/iodine/http.h +33 -10
  27. data/ext/iodine/http1.c +78 -42
  28. data/ext/iodine/http_request.c +6 -0
  29. data/ext/iodine/http_request.h +3 -0
  30. data/ext/iodine/http_response.c +43 -11
  31. data/ext/iodine/iodine.c +380 -0
  32. data/ext/iodine/iodine.h +62 -0
  33. data/ext/iodine/iodine_helpers.c +235 -0
  34. data/ext/iodine/iodine_helpers.h +13 -0
  35. data/ext/iodine/iodine_http.c +409 -241
  36. data/ext/iodine/iodine_http.h +7 -14
  37. data/ext/iodine/iodine_protocol.c +626 -0
  38. data/ext/iodine/iodine_protocol.h +13 -0
  39. data/ext/iodine/iodine_pubsub.c +646 -0
  40. data/ext/iodine/iodine_pubsub.h +27 -0
  41. data/ext/iodine/iodine_websockets.c +796 -0
  42. data/ext/iodine/iodine_websockets.h +19 -0
  43. data/ext/iodine/pubsub.c +544 -0
  44. data/ext/iodine/pubsub.h +215 -0
  45. data/ext/iodine/random.c +4 -4
  46. data/ext/iodine/rb-call.c +1 -5
  47. data/ext/iodine/rb-defer.c +3 -20
  48. data/ext/iodine/rb-rack-io.c +22 -22
  49. data/ext/iodine/rb-rack-io.h +3 -4
  50. data/ext/iodine/rb-registry.c +111 -118
  51. data/ext/iodine/redis_connection.c +277 -0
  52. data/ext/iodine/redis_connection.h +77 -0
  53. data/ext/iodine/redis_engine.c +398 -0
  54. data/ext/iodine/redis_engine.h +68 -0
  55. data/ext/iodine/resp.c +842 -0
  56. data/ext/iodine/resp.h +253 -0
  57. data/ext/iodine/sock.c +26 -12
  58. data/ext/iodine/sock.h +14 -3
  59. data/ext/iodine/spnlock.inc +19 -2
  60. data/ext/iodine/websockets.c +299 -11
  61. data/ext/iodine/websockets.h +159 -6
  62. data/lib/iodine.rb +104 -1
  63. data/lib/iodine/cli.rb +106 -0
  64. data/lib/iodine/monkeypatch.rb +40 -0
  65. data/lib/iodine/pubsub.rb +70 -0
  66. data/lib/iodine/version.rb +1 -1
  67. data/lib/iodine/websocket.rb +12 -0
  68. data/lib/rack/handler/iodine.rb +33 -7
  69. metadata +35 -7
  70. data/ext/iodine/iodine_core.c +0 -760
  71. data/ext/iodine/iodine_core.h +0 -79
  72. data/ext/iodine/iodine_websocket.c +0 -551
  73. data/ext/iodine/iodine_websocket.h +0 -22
  74. data/lib/iodine/http.rb +0 -4
@@ -0,0 +1,70 @@
1
+ module Iodine
2
+ # Iodine is equiped with an internal pub/sub service that allows improved resource management from a deployment perspective.
3
+ #
4
+ # As described by [Wikipedia](https://en.wikipedia.org/wiki/Publish–subscribe_pattern):
5
+ #
6
+ # publish–subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead characterize published messages into classes without knowledge of which subscribers, if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.
7
+ #
8
+ # The common paradigm, which is implemented by pub/sub services like Redis,
9
+ # is for a "client" to "subscribe" to one or more "channels". Messages are streamed
10
+ # to these "channels" by different "publishers" (the application / other clients) and are
11
+ # broadcasted to the "clients" through their "subscription".
12
+ #
13
+ # Iodine's approach it to offload pub/sub resource costs from the pub/sub service
14
+ # (which is usually expensive to scale) onto the application layer.
15
+ #
16
+ # For example, the default (`nil`) pub/sub {Iodine::PubSub::Engine} implements
17
+ # an internal pub/sub service that manages subscriptions (clients and channels) throughout an Iodine process cluster without any need to connect to an external pub/sub service.
18
+ #
19
+ # If Iodine was runninng with 8 processes and 16 threads per process,
20
+ # a publishing in process A will be delivered to subscribers in process B.
21
+ #
22
+ # In addition, by inheriting the {Iodine::PubSub::Engine} class, it's easy to create pub/sub engines that connect to this
23
+ # underlying pub/sub service. This means that Iodine will call the engine's `subscribe` method only once per
24
+ # channel and once messages arrive, Iodine will distribute the messages to all the subscribed clients.
25
+ module PubSub
26
+ # The {Iodine::PubSub::Engine} class makes it easy to use leverage Iodine's pub/sub system using external services.
27
+ #
28
+ # Iodine comes with two built-in engines:
29
+ #
30
+ # * `Iodine::PubSub::Engine::CLUSTER` will distribute messages to all subscribers in the process cluster.
31
+ # * `Iodine::PubSub::Engine::SINGLE_PROCESS` will distribute messages to all subscribers sharing the same process.
32
+ #
33
+ # {Iodine::PubSub::Engine} instances should be initialized only after Iodine
34
+ # started running (or the `fork`ing of the engine's connection will introduce communication issues).
35
+ #
36
+ # For this reason, the best approcah to initialization would be:
37
+ #
38
+ # class MyEngineClass < Iodine::PubSub::Engine
39
+ # # ...
40
+ # end
41
+ #
42
+ # Iodine.run do
43
+ # MyEngine = MyEngineClass.new
44
+ # end
45
+ #
46
+ # {Iodine::PubSub::Engine} child classes MUST override the {Iodine::PubSub::Engine#subscribe},
47
+ # {Iodine::PubSub::Engine#unsubscribe} and {Iodine::PubSub::Engine#publish}
48
+ # in order to perform this actions using the backend service (i.e. using Redis).
49
+ #
50
+ # Once an {Iodine::PubSub::Engine} instance receives a message from the backend service,
51
+ # it should forward the message to the Iodine distribution layer using the {Iodine::PubSub::Engine#distribute} method.
52
+ #
53
+ # Iodine will than distribute the message to all registered clients.
54
+ #
55
+ # *IMPORTANT*:
56
+ #
57
+ # Connections shouldn't call any of the {Iodine::PubSub::Engine} or {Iodine::PubSub} methods directly,
58
+ # since connections might be disconnected at any time, at which point their memory will become corrupted or recycled.
59
+ #
60
+ # The connection pub/sub methods (coming soon) keep the application safe from these
61
+ # risks by performing specific checks for connection related pub/sub actions.
62
+ #
63
+ class Engine
64
+ end
65
+ # This is the (currently) default pub/sub engine. It will distribute messages to all subscribers in the process cluster.
66
+ CLUSTER
67
+ # This is a single process pub/sub engine. It will distribute messages to all subscribers sharing the same process.
68
+ SINGLE_PROCESS
69
+ end
70
+ end
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.3.6'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -0,0 +1,12 @@
1
+ module Iodine
2
+ # This module lists the available API for Websocket connections and classes.
3
+ #
4
+ # This module is mixed in (using `extend` and `include`) with the Websocket Callback Object (as specified by the {file:SPEC-Websocket-Draft.md proposed Rack specification}.
5
+ #
6
+ # The websocket API is divided into three main groups:
7
+ # * Server <=> Client relations ({Iodine::Websocket#write}, {Iodine::Websocket#close} etc')
8
+ # * Client <=> Server <=> Client relations ({Iodine::Websocket#subscribe}, {Iodine::Websocket#publish}, etc').
9
+ # * Task scheduling ({Iodine::Websocket.defer}, {Iodine::Websocket#defer}, {Iodine::Websocket.each}).
10
+ module Websocket
11
+ end
12
+ end
@@ -1,7 +1,9 @@
1
1
  require 'iodine' unless defined?(::Iodine::VERSION)
2
2
 
3
3
  module Iodine
4
- # {Iodine::Rack} is an Iodine HTTP and Websocket Rack server bundled with {Iodine} for your convinience.
4
+ # {Iodine::Rack} provides a Rack complient interface (connecting Iodine to Rack) for an HTTP and Websocket Server.
5
+ #
6
+ # {Iodine::Rack} also provides convinient access to the {Iodine::HTTP.listen} function, which powers the {Iodine::Rack} server.
5
7
  module Rack
6
8
  # get/set the Rack application.
7
9
  def self.app=(val)
@@ -46,22 +48,42 @@ module Iodine
46
48
 
47
49
  # get/set the maximum HTTP body size for incoming data. Defaults to ~50Mb. 0 values are silently ignored.
48
50
  def self.max_body_size=(val)
49
- @max_body_size = val
51
+ @max_body = val
50
52
  end
51
53
 
52
54
  # get/set the maximum HTTP body size for incoming data. Defaults to ~50Mb.
53
55
  def self.max_body_size
54
- @max_body_size
56
+ @max_body
57
+ end
58
+
59
+ # get/set the maximum HTTP body size for incoming data. Defaults to ~50Mb. 0 values are silently ignored.
60
+ def self.max_body=(val)
61
+ @max_body = val
62
+ end
63
+
64
+ # get/set the maximum HTTP body size for incoming data. Defaults to ~50Mb.
65
+ def self.max_body
66
+ @max_body
55
67
  end
56
68
 
57
69
  # get/set the maximum Websocket body size for incoming data. Defaults to defaults to ~250KB. 0 values are silently ignored.
58
70
  def self.max_msg_size=(val)
59
- @max_msg_size = val
71
+ @max_msg = val
60
72
  end
61
73
 
62
74
  # get/set the maximum Websocket body size for incoming data. Defaults to defaults to ~250KB. 0 values are silently ignored.
63
75
  def self.max_msg_size
64
- @max_msg_size
76
+ @max_msg
77
+ end
78
+
79
+ # get/set the maximum Websocket body size for incoming data. Defaults to defaults to ~250KB. 0 values are silently ignored.
80
+ def self.max_msg=(val)
81
+ @max_msg = val
82
+ end
83
+
84
+ # get/set the maximum Websocket body size for incoming data. Defaults to defaults to ~250KB. 0 values are silently ignored.
85
+ def self.max_msg
86
+ @max_msg
65
87
  end
66
88
 
67
89
  # get/set the HTTP logging value (true / false). Defaults to the incoming argumrnts or `false`.
@@ -114,13 +136,17 @@ module Iodine
114
136
  end
115
137
  @port = options[:Port].to_s if options[:Port]
116
138
  @port = options[:Address].to_s if options[:Address]
117
- # provide Websocket features using Rack::Websocket
139
+
140
+ # # provide Websocket features using Rack::Websocket
118
141
  # Rack.send :remove_const, :Websocket if defined?(Rack::Websocket)
119
142
  # Rack.const_set :Websocket, ::Iodine::Websocket
143
+
120
144
  # start Iodine
121
145
  Iodine.start
122
- # remove the Websocket features from Rack::Websocket
146
+
147
+ # # remove the Websocket features from Rack::Websocket
123
148
  # Rack.send :remove_const, :Websocket
149
+
124
150
  true
125
151
  end
126
152
  IODINE_RACK_LOADED = true
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.3.6
4
+ version: 0.4.0
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-06-06 00:00:00.000000000 Z
11
+ date: 2017-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -95,6 +95,7 @@ files:
95
95
  - CHANGELOG.md
96
96
  - Gemfile
97
97
  - LICENSE.txt
98
+ - LIMITS.md
98
99
  - README.md
99
100
  - Rakefile
100
101
  - SPEC-Websocket-Draft.md
@@ -114,9 +115,15 @@ files:
114
115
  - bin/raw-rbhttp-em
115
116
  - bin/raw_broadcast
116
117
  - bin/test_with_faye
118
+ - bin/updated api
117
119
  - bin/ws-broadcast
118
120
  - bin/ws-echo
119
121
  - bin/ws-shootout
122
+ - examples/broadcast.ru
123
+ - examples/echo.ru
124
+ - examples/hello.ru
125
+ - examples/redis.ru
126
+ - examples/shootout.ru
120
127
  - exe/iodine
121
128
  - ext/iodine/base64.c
122
129
  - ext/iodine/base64.h
@@ -124,11 +131,15 @@ files:
124
131
  - ext/iodine/bscrypt.h
125
132
  - ext/iodine/defer.c
126
133
  - ext/iodine/defer.h
134
+ - ext/iodine/empty.h
127
135
  - ext/iodine/evio.c
128
136
  - ext/iodine/evio.h
129
137
  - ext/iodine/extconf.rb
130
138
  - ext/iodine/facil.c
131
139
  - ext/iodine/facil.h
140
+ - ext/iodine/fio_dict.c
141
+ - ext/iodine/fio_dict.h
142
+ - ext/iodine/fio_hash_table.h
132
143
  - ext/iodine/fio_list.h
133
144
  - ext/iodine/hex.c
134
145
  - ext/iodine/hex.h
@@ -146,14 +157,22 @@ files:
146
157
  - ext/iodine/http_request.h
147
158
  - ext/iodine/http_response.c
148
159
  - ext/iodine/http_response.h
149
- - ext/iodine/iodine_core.c
150
- - ext/iodine/iodine_core.h
160
+ - ext/iodine/iodine.c
161
+ - ext/iodine/iodine.h
162
+ - ext/iodine/iodine_helpers.c
163
+ - ext/iodine/iodine_helpers.h
151
164
  - ext/iodine/iodine_http.c
152
165
  - ext/iodine/iodine_http.h
153
- - ext/iodine/iodine_websocket.c
154
- - ext/iodine/iodine_websocket.h
166
+ - ext/iodine/iodine_protocol.c
167
+ - ext/iodine/iodine_protocol.h
168
+ - ext/iodine/iodine_pubsub.c
169
+ - ext/iodine/iodine_pubsub.h
170
+ - ext/iodine/iodine_websockets.c
171
+ - ext/iodine/iodine_websockets.h
155
172
  - ext/iodine/misc.c
156
173
  - ext/iodine/misc.h
174
+ - ext/iodine/pubsub.c
175
+ - ext/iodine/pubsub.h
157
176
  - ext/iodine/random.c
158
177
  - ext/iodine/random.h
159
178
  - ext/iodine/rb-call.c
@@ -163,6 +182,12 @@ files:
163
182
  - ext/iodine/rb-rack-io.h
164
183
  - ext/iodine/rb-registry.c
165
184
  - ext/iodine/rb-registry.h
185
+ - ext/iodine/redis_connection.c
186
+ - ext/iodine/redis_connection.h
187
+ - ext/iodine/redis_engine.c
188
+ - ext/iodine/redis_engine.h
189
+ - ext/iodine/resp.c
190
+ - ext/iodine/resp.h
166
191
  - ext/iodine/sha1.c
167
192
  - ext/iodine/sha1.h
168
193
  - ext/iodine/sha2.c
@@ -178,9 +203,12 @@ files:
178
203
  - ext/iodine/xor-crypt.h
179
204
  - iodine.gemspec
180
205
  - lib/iodine.rb
181
- - lib/iodine/http.rb
206
+ - lib/iodine/cli.rb
207
+ - lib/iodine/monkeypatch.rb
182
208
  - lib/iodine/protocol.rb
209
+ - lib/iodine/pubsub.rb
183
210
  - lib/iodine/version.rb
211
+ - lib/iodine/websocket.rb
184
212
  - lib/rack/handler/iodine.rb
185
213
  - logo.png
186
214
  homepage: https://github.com/boazsegev/iodine
@@ -1,760 +0,0 @@
1
- /*
2
- Copyright: Boaz segev, 2016-2017
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #include "iodine_core.h"
8
- #include "iodine_http.h"
9
-
10
- /* *****************************************************************************
11
- Core data
12
- */
13
-
14
- /* these should be made globally accessible to any Iodine module */
15
- rb_encoding *BinaryEncoding;
16
- rb_encoding *UTF8Encoding;
17
- int BinaryEncodingIndex;
18
- int UTF8EncodingIndex;
19
- VALUE Iodine;
20
- VALUE IodineBase;
21
- VALUE Iodine_Version;
22
- const char *Iodine_Version_Str;
23
- ID call_proc_id;
24
- ID on_start_func_id;
25
- ID on_finish_func_id;
26
- ID new_func_id;
27
- ID on_open_func_id;
28
- ID on_message_func_id;
29
- ID on_data_func_id;
30
- ID on_ready_func_id;
31
- ID on_shutdown_func_id;
32
- ID on_close_func_id;
33
- ID ping_func_id;
34
- ID buff_var_id;
35
- ID fd_var_id;
36
- ID timeout_var_id;
37
- ID to_s_method_id;
38
-
39
- /* local core data variables */
40
- static VALUE DynamicProtocol;
41
- static VALUE DynamicProtocolClass;
42
- /* *****************************************************************************
43
- The Core dynamic Iodine protocol
44
- */
45
-
46
- static const char *iodine_protocol_service = "IodineDynamicProtocol";
47
-
48
- /* *****************************************************************************
49
- The Core dynamic Iodine protocol methods and helpers
50
- */
51
-
52
- /**
53
- Reads `n` bytes from the network connection.
54
- The number of bytes to be read (n) is:
55
- - the number of bytes set in the optional `buffer_or_length` argument.
56
- - the String capacity (not length) of the String passed as the optional
57
- `buffer_or_length` argument.
58
- - 1024 Bytes (1Kb) if the optional `buffer_or_length` is either missing or
59
- contains a String who's capacity is less then 1Kb.
60
- Returns a String (either the same one used as the buffer or a new one) on a
61
- successful read. Returns `nil` if no data was available.
62
- */
63
- static VALUE dyn_read(int argc, VALUE *argv, VALUE self) {
64
- if (argc > 1) {
65
- rb_raise(
66
- rb_eArgError,
67
- "read accepts only one argument - a Fixnum (buffer length) or a String "
68
- "(it's capacity - or 1Kb, whichever's the higher - will be used as "
69
- "buffer's length).");
70
- return Qnil;
71
- }
72
- VALUE buffer = (argc == 1 ? argv[0] : Qnil);
73
- if (buffer != Qnil && TYPE(buffer) != T_FIXNUM && TYPE(buffer) != T_STRING) {
74
- rb_raise(rb_eTypeError,
75
- "buffer should either be a length (a new string will be created) "
76
- "or a string (reading will be limited to the original string's "
77
- "capacity or 1Kb - whichever the larger).");
78
- return Qnil;
79
- }
80
- VALUE str;
81
- long len;
82
- intptr_t fd = iodine_get_fd(self);
83
- if (buffer == Qnil) {
84
- buffer = LONG2FIX(1024);
85
- }
86
- if (TYPE(buffer) == T_FIXNUM) {
87
- len = FIX2LONG(buffer);
88
- if (len <= 0)
89
- len = 1024;
90
- str = rb_str_buf_new(len);
91
- // create a rb_String with X length and take it's pointer
92
- // rb_str_resize(VALUE str, long len)
93
- // RSTRING_PTR(str)
94
- } else {
95
- // take the string's pointer and length
96
- len = rb_str_capacity(buffer);
97
- // make sure the string is modifiable
98
- rb_str_modify(buffer);
99
- // resize the string if needed.
100
- if (len < 1024)
101
- rb_str_resize(buffer, (len = 1024));
102
- str = buffer;
103
- }
104
- ssize_t in = sock_read(fd, RSTRING_PTR(str), len);
105
- // make sure it's binary encoded
106
- rb_enc_associate_index(str, BinaryEncodingIndex);
107
- // set actual size....
108
- if (in > 0)
109
- rb_str_set_len(str, (long)in);
110
- else {
111
- rb_str_set_len(str, 0);
112
- str = Qnil;
113
- }
114
- // return empty string? or fix above if to return Qnil?
115
- return str;
116
- }
117
-
118
- /**
119
- Writes data to the connection. Returns `false` on error and `self` on success.
120
- */
121
- static VALUE dyn_write(VALUE self, VALUE data) {
122
- intptr_t fd = iodine_get_fd(self);
123
- if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data)))
124
- return Qfalse;
125
- return self;
126
- }
127
-
128
- /**
129
- Writes data to the connection. The data will be sent as soon as possible without
130
- fragmantation of previously scheduled data.
131
-
132
- Returns `false` on error and `self` on success.
133
- */
134
- static VALUE dyn_write_urgent(VALUE self, VALUE data) {
135
- intptr_t fd = iodine_get_fd(self);
136
- if (sock_write2(.uuid = fd, .buffer = RSTRING(data),
137
- .length = RSTRING_LEN(data), .urgent = 1))
138
- return Qfalse;
139
- return self;
140
- }
141
-
142
- /**
143
- Update's a connection's timeout.
144
-
145
- Returns self.
146
- */
147
- static VALUE dyn_set_timeout(VALUE self, VALUE timeout) {
148
- intptr_t fd = iodine_get_fd(self);
149
- unsigned int tout = FIX2UINT(timeout);
150
- if (tout > 255)
151
- tout = 255;
152
- facil_set_timeout(fd, tout);
153
- return self;
154
- }
155
-
156
- /**
157
- Returns the connection's timeout.
158
- */
159
- static VALUE dyn_get_timeout(VALUE self) {
160
- intptr_t fd = iodine_get_fd(self);
161
- uint8_t tout = facil_get_timeout(fd);
162
- unsigned int tout_int = tout;
163
- return UINT2NUM(tout_int);
164
- }
165
-
166
- /**
167
- Closes a connection.
168
-
169
- The connection will be closed only once all the data was sent.
170
-
171
- Returns self.
172
- */
173
- static VALUE dyn_close(VALUE self) {
174
- intptr_t fd = iodine_get_fd(self);
175
- sock_close(fd);
176
- return self;
177
- }
178
-
179
- /* *****************************************************************************
180
- The Core dynamic Iodine protocol task implementation
181
- */
182
-
183
- static void dyn_perform_defer(intptr_t uuid, protocol_s *protocol, void *arg) {
184
- (void)(uuid);
185
- (void)(protocol);
186
- RubyCaller.call((VALUE)arg, call_proc_id);
187
- Registry.remove((VALUE)arg);
188
- }
189
- static void dyn_defer_fallback(intptr_t uuid, void *arg) {
190
- (void)(uuid);
191
- Registry.remove((VALUE)arg);
192
- }
193
-
194
- /**
195
- Runs the required block later (defers the blocks execution).
196
-
197
- Unlike {Iodine#run}, the block will **not* run concurrently with any other
198
- callback
199
- for this object (except `ping` and `on_ready`).
200
-
201
- Also, unlike {Iodine#run}, the block will **not** be called unless the
202
- connection remains open at the time it's execution is scheduled.
203
-
204
- Always returns `self`.
205
- */
206
- static VALUE dyn_defer(VALUE self) {
207
- // requires a block to be passed
208
- rb_need_block();
209
- VALUE block = rb_block_proc();
210
- if (block == Qnil)
211
- return Qfalse;
212
- Registry.add(block);
213
- intptr_t fd = iodine_get_fd(self);
214
- facil_defer(.uuid = fd, .task = dyn_perform_defer, .arg = (void *)block,
215
- .fallback = dyn_defer_fallback);
216
- return self;
217
- }
218
-
219
- static void dyn_perform_each_task(intptr_t fd, protocol_s *protocol,
220
- void *data) {
221
- (void)(fd);
222
- RubyCaller.call2((VALUE)data, call_proc_id, 1,
223
- &(dyn_prot(protocol)->handler));
224
- }
225
- static void dyn_finish_each_task(intptr_t fd, void *data) {
226
- (void)(fd);
227
- Registry.remove((VALUE)data);
228
- }
229
-
230
- void iodine_run_each(intptr_t origin, const char *service, VALUE block) {
231
- facil_each(.origin = origin, .service = service,
232
- .task = dyn_perform_each_task, .arg = (void *)block,
233
- .on_complete = dyn_finish_each_task);
234
- }
235
-
236
- /**
237
- Runs the required block for each dynamic protocol connection except this one.
238
-
239
- Tasks will be performed within each connections lock, so no connection will have
240
- more then one task being performed at the same time (similar to {#defer}).
241
-
242
- Also, unlike {Iodine.run}, the block will **not** be called unless the
243
- connection remains open at the time it's execution is scheduled.
244
-
245
- Always returns `self`.
246
- */
247
- static VALUE dyn_each(VALUE self) {
248
- // requires a block to be passed
249
- rb_need_block();
250
- VALUE block = rb_block_proc();
251
- if (block == Qnil)
252
- return Qfalse;
253
- Registry.add(block);
254
- intptr_t fd = iodine_get_fd(self);
255
- facil_each(.origin = fd, .service = iodine_protocol_service,
256
- .task = dyn_perform_each_task, .arg = (void *)block,
257
- .on_complete = dyn_finish_each_task);
258
- return self;
259
- }
260
-
261
- /**
262
- Runs the required block for each dynamic protocol connection.
263
-
264
- Tasks will be performed within each connections lock, so no connection will have
265
- more then one task being performed at the same time (similar to {#defer}).
266
-
267
- Also, unlike {Iodine.run}, the block will **not** be called unless the
268
- connection remains open at the time it's execution is scheduled.
269
-
270
- Always returns `self`.
271
- */
272
- static VALUE dyn_class_each(VALUE self) {
273
- // requires a block to be passed
274
- rb_need_block();
275
- VALUE block = rb_block_proc();
276
- if (block == Qnil)
277
- return Qfalse;
278
- Registry.add(block);
279
- facil_each(.origin = -1, .service = iodine_protocol_service,
280
- .task = dyn_perform_each_task, .arg = (void *)block,
281
- .on_complete = dyn_finish_each_task);
282
- return self;
283
- }
284
-
285
- /* will be defined in detail later, after some more functions were written */
286
- VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler);
287
-
288
- VALUE dyn_upgrade(VALUE self, VALUE handler) {
289
- return iodine_upgrade2basic(iodine_get_fd(self), handler);
290
- }
291
-
292
- /* *****************************************************************************
293
- The Core dynamic Iodine protocol bridge
294
- */
295
-
296
- /** Implement this callback to handle the event. The default implementation will
297
- * close the connection. */
298
- static VALUE not_implemented_ping(VALUE self) {
299
- sock_close(iodine_get_fd(self));
300
- return Qnil;
301
- }
302
- /** implement this callback to handle the event. */
303
- static VALUE not_implemented(VALUE self) {
304
- (void)(self);
305
- return Qnil;
306
- }
307
- /** implement this callback to handle the event. */
308
- static VALUE not_implemented2(VALUE self, VALUE data) {
309
- (void)(self);
310
- (void)(data);
311
- return Qnil;
312
- }
313
-
314
- /**
315
- A default on_data implementation will read up to 1Kb into a reusable buffer from
316
- the socket and call the `on_message` callback.
317
-
318
- It is recommended that you implement this callback if messages might require
319
- more then 1Kb of space.
320
- */
321
- static VALUE default_on_data(VALUE self) {
322
- VALUE buff = rb_ivar_get(self, buff_var_id);
323
- if (buff == Qnil) {
324
- rb_ivar_set(self, buff_var_id, (buff = rb_str_buf_new(1024)));
325
- }
326
- do {
327
- dyn_read(1, &buff, self);
328
- if (!RSTRING_LEN(buff))
329
- return Qnil;
330
- rb_funcall(self, on_message_func_id, 1, buff);
331
- } while (RSTRING_LEN(buff) == (ssize_t)rb_str_capacity(buff));
332
- return Qnil;
333
- }
334
-
335
- /** called when a data is available, but will not run concurrently */
336
- static void dyn_protocol_on_data(intptr_t fduuid, protocol_s *protocol) {
337
- (void)(fduuid);
338
- RubyCaller.call(dyn_prot(protocol)->handler, on_data_func_id);
339
- }
340
- /** called when the socket is ready to be written to. */
341
- static void dyn_protocol_on_ready(intptr_t fduuid, protocol_s *protocol) {
342
- (void)(fduuid);
343
- RubyCaller.call(dyn_prot(protocol)->handler, on_ready_func_id);
344
- }
345
- /** called when the server is shutting down,
346
- * but before closing the connection. */
347
- static void dyn_protocol_on_shutdown(intptr_t fduuid, protocol_s *protocol) {
348
- (void)(fduuid);
349
- RubyCaller.call(dyn_prot(protocol)->handler, on_shutdown_func_id);
350
- }
351
- /** called when the connection was closed, but will not run concurrently */
352
- static void dyn_protocol_on_close(protocol_s *protocol) {
353
- RubyCaller.call(dyn_prot(protocol)->handler, on_close_func_id);
354
- Registry.remove(dyn_prot(protocol)->handler);
355
- free(protocol);
356
- }
357
- /** called when a connection's timeout was reached */
358
- static void dyn_protocol_ping(intptr_t fduuid, protocol_s *protocol) {
359
- (void)(fduuid);
360
- RubyCaller.call(dyn_prot(protocol)->handler, ping_func_id);
361
- }
362
- /** Update's a connection's handler and timeout. */
363
- static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
364
- uint8_t timeout) {
365
- Registry.add(handler);
366
- iodine_set_fd(handler, fduuid);
367
- dyn_protocol_s *protocol = malloc(sizeof(*protocol));
368
- if (protocol == NULL) {
369
- Registry.remove(handler);
370
- return NULL;
371
- }
372
- facil_set_timeout(fduuid, timeout);
373
- *protocol = (dyn_protocol_s){
374
- .handler = handler,
375
- .protocol.on_data = dyn_protocol_on_data,
376
- .protocol.on_close = dyn_protocol_on_close,
377
- .protocol.on_shutdown = dyn_protocol_on_shutdown,
378
- .protocol.on_ready = dyn_protocol_on_ready,
379
- .protocol.ping = dyn_protocol_ping,
380
- .protocol.service = iodine_protocol_service,
381
- };
382
- RubyCaller.call(handler, on_open_func_id);
383
- return (protocol_s *)protocol;
384
- }
385
-
386
- static protocol_s *on_open_dyn_protocol(intptr_t fduuid, void *udata) {
387
- VALUE rb_tout = rb_ivar_get((VALUE)udata, timeout_var_id);
388
- uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
389
- VALUE handler = RubyCaller.call((VALUE)udata, new_func_id);
390
- if (handler == Qnil)
391
- return NULL;
392
- return dyn_set_protocol(fduuid, handler, timeout);
393
- }
394
-
395
- /** called once, when Iodine starts running. */
396
- static void on_server_start_for_handler(void *udata) {
397
- RubyCaller.call((VALUE)udata, on_start_func_id);
398
- }
399
- /** called once, when Iodine stops running. */
400
- static void on_server_on_finish_for_handler(void *udata) {
401
- RubyCaller.call((VALUE)udata, on_finish_func_id);
402
- }
403
-
404
- void Init_DynamicProtocol(void) {
405
- /**
406
- The Protocol module is included in any object or class that handles an Iodine
407
- connection using a custom / dynamic protocol (not the Websockets or HTTP
408
- protocols that Iodine supports natively).
409
- */
410
- DynamicProtocolClass = rb_define_module_under(IodineBase, "ProtocolClass");
411
- rb_define_method(DynamicProtocolClass, "on_start", not_implemented, 0);
412
- rb_define_method(DynamicProtocolClass, "on_finish", not_implemented, 0);
413
- rb_define_method(DynamicProtocolClass, "each", dyn_class_each, 0);
414
-
415
- DynamicProtocol = rb_define_module_under(Iodine, "Protocol");
416
- rb_define_method(DynamicProtocol, "on_open", not_implemented, 0);
417
- rb_define_method(DynamicProtocol, "on_close", not_implemented, 0);
418
- rb_define_method(DynamicProtocol, "on_message", not_implemented2, 1);
419
- rb_define_method(DynamicProtocol, "on_data", default_on_data, 0);
420
- rb_define_method(DynamicProtocol, "on_ready", not_implemented, 0);
421
- rb_define_method(DynamicProtocol, "on_shutdown", not_implemented, 0);
422
- rb_define_method(DynamicProtocol, "ping", not_implemented_ping, 0);
423
-
424
- // helper methods
425
- rb_define_method(DynamicProtocol, "read", dyn_read, -1);
426
- rb_define_method(DynamicProtocol, "write", dyn_write, 1);
427
- rb_define_method(DynamicProtocol, "write_urgent", dyn_write_urgent, 1);
428
- rb_define_method(DynamicProtocol, "close", dyn_close, 0);
429
- rb_define_method(DynamicProtocol, "defer", dyn_defer, 0);
430
- rb_define_method(DynamicProtocol, "each", dyn_each, 0);
431
- rb_define_method(DynamicProtocol, "upgrade", dyn_upgrade, 1);
432
- rb_define_method(DynamicProtocol, "timeout=", dyn_set_timeout, 1);
433
- rb_define_method(DynamicProtocol, "timeout", dyn_get_timeout, 0);
434
- }
435
-
436
- /* *****************************************************************************
437
- Iodine functions
438
- */
439
-
440
- /** Sets up a listening socket. Conncetions received at the assigned port will
441
- be handled by the assigned handler.
442
-
443
- Multiple services (listening sockets) can be registered before starting the
444
- Iodine event loop. */
445
- static VALUE iodine_listen_dyn_protocol(VALUE self, VALUE port, VALUE handler) {
446
- // validate that the handler is a class and include the Iodine::Protocol
447
- if (TYPE(handler) == T_CLASS) {
448
- // include the Protocol module
449
- // // do we neet to check?
450
- // if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
451
- rb_include_module(handler, DynamicProtocol);
452
- rb_extend_object(handler, DynamicProtocolClass);
453
- } else {
454
- rb_raise(rb_eTypeError, "The connection handler MUST be of type Class.");
455
- return Qnil;
456
- }
457
- if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
458
- rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
459
- if (TYPE(port) == T_FIXNUM)
460
- port = rb_funcall2(port, to_s_method_id, 0, NULL);
461
- rb_ivar_set(self, rb_intern("_port"), port);
462
- // listen
463
- facil_listen(.port = StringValueCStr(port), .udata = (void *)handler,
464
- .on_open = on_open_dyn_protocol,
465
- .on_start = on_server_start_for_handler,
466
- .on_finish = on_server_on_finish_for_handler);
467
- return self;
468
- }
469
-
470
- VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler) {
471
- uint8_t timeout;
472
- if (TYPE(handler) == T_CLASS) {
473
- // get the timeout
474
- VALUE rb_tout = rb_ivar_get(handler, timeout_var_id);
475
- timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
476
- // include the Protocol module
477
- // // do we neet to check?
478
- // if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
479
- rb_include_module(handler, DynamicProtocol);
480
- handler = RubyCaller.call(handler, new_func_id);
481
- } else {
482
- // include the Protocol module in the object's class
483
- VALUE p_class = rb_obj_class(handler);
484
- // // do we neet to check?
485
- // if (rb_mod_include_p(p_class, rDynProtocol) == Qfalse)
486
- rb_include_module(p_class, DynamicProtocol);
487
- // get the timeout
488
- VALUE rb_tout = rb_ivar_get(p_class, timeout_var_id);
489
- if (rb_tout == Qnil)
490
- rb_tout = rb_ivar_get(handler, timeout_var_id);
491
- timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
492
- }
493
- protocol_s *protocol = dyn_set_protocol(fduuid, handler, timeout);
494
- if (protocol) {
495
- if (facil_attach(fduuid, protocol))
496
- dyn_protocol_on_close(protocol);
497
- return handler;
498
- }
499
- return Qfalse;
500
- }
501
-
502
- /* *****************************************************************************
503
- Iodine Task Management
504
- */
505
-
506
- static void iodine_run_once2(void *block, void *ignr) {
507
- (void)ignr;
508
- RubyCaller.call((VALUE)block, call_proc_id);
509
- Registry.remove((VALUE)block);
510
- }
511
- static void iodine_run_once(void *block) {
512
- RubyCaller.call((VALUE)block, call_proc_id);
513
- Registry.remove((VALUE)block);
514
- }
515
-
516
- static void iodine_run_always(void *block) {
517
- RubyCaller.call((VALUE)block, call_proc_id);
518
- }
519
-
520
- /**
521
- Runs the required block later. The block might run concurrently with the
522
- existing code (depending on the amount and availability of worker threads).
523
-
524
- Returns the block object. The block will run only while Iodine is running (run
525
- will be delayed until Iodine.start is called, unless Iodine's event loop is
526
- active).
527
- */
528
- static VALUE iodine_run_async(VALUE self) {
529
- (void)(self);
530
- // requires a block to be passed
531
- rb_need_block();
532
- VALUE block = rb_block_proc();
533
- if (block == Qnil)
534
- return Qfalse;
535
- Registry.add(block);
536
- if (defer(iodine_run_once2, (void *)block, NULL))
537
- perror("ERROR: dropped defered task");
538
- return block;
539
- }
540
-
541
- /**
542
- Runs the required block after the specified number of milliseconds have passed.
543
- Time is counted only once Iodine started running (using {Iodine.start}).
544
-
545
- Always returns a copy of the block object.
546
- */
547
- static VALUE iodine_run_after(VALUE self, VALUE milliseconds) {
548
- (void)(self);
549
- if (TYPE(milliseconds) != T_FIXNUM) {
550
- rb_raise(rb_eTypeError, "milliseconds must be a number");
551
- return Qnil;
552
- }
553
- size_t milli = FIX2UINT(milliseconds);
554
- // requires a block to be passed
555
- rb_need_block();
556
- VALUE block = rb_block_proc();
557
- if (block == Qnil)
558
- return Qfalse;
559
- Registry.add(block);
560
- facil_run_every(milli, 1, iodine_run_once, (void *)block, NULL);
561
- return block;
562
- }
563
- /**
564
- Runs the required block after the specified number of milliseconds have passed.
565
- Time is counted only once Iodine started running (using {Iodine.start}).
566
-
567
- Accepts:
568
-
569
- milliseconds:: the number of milliseconds between event repetitions.
570
-
571
- repetitions:: the number of event repetitions. Defaults to 0 (never ending).
572
-
573
- block:: (required) a block is required, as otherwise there is nothing to
574
- perform.
575
-
576
- The event will repeat itself until the number of repetitions had been delpeted.
577
-
578
- Always returns a copy of the block object.
579
- */
580
- static VALUE iodine_run_every(int argc, VALUE *argv, VALUE self) {
581
- (void)(self);
582
- VALUE milliseconds, repetitions, block;
583
-
584
- rb_scan_args(argc, argv, "11&", &milliseconds, &repetitions, &block);
585
-
586
- if (TYPE(milliseconds) != T_FIXNUM) {
587
- rb_raise(rb_eTypeError, "milliseconds must be a number.");
588
- return Qnil;
589
- }
590
- if (repetitions != Qnil && TYPE(repetitions) != T_FIXNUM) {
591
- rb_raise(rb_eTypeError, "repetitions must be a number or `nil`.");
592
- return Qnil;
593
- }
594
-
595
- size_t milli = FIX2UINT(milliseconds);
596
- size_t repeat = (repetitions == Qnil) ? 0 : FIX2UINT(repetitions);
597
- // requires a block to be passed
598
- rb_need_block();
599
- Registry.add(block);
600
- facil_run_every(milli, repeat, iodine_run_always, (void *)block,
601
- (void (*)(void *))Registry.remove);
602
- return block;
603
- }
604
-
605
- static VALUE iodine_count(VALUE self) {
606
- (void)(self);
607
- return ULONG2NUM(facil_count(NULL));
608
- }
609
- /* *****************************************************************************
610
- Running the server
611
- */
612
- #include "spnlock.inc"
613
- #include <pthread.h>
614
- static volatile int sock_io_thread = 0;
615
- static pthread_t sock_io_pthread;
616
-
617
- static void *iodine_io_thread(void *arg) {
618
- (void)arg;
619
- struct timespec tm;
620
- // static const struct timespec tm = {.tv_nsec = 524288UL};
621
- while (sock_io_thread) {
622
- sock_flush_all();
623
- tm = (struct timespec){.tv_nsec = 524288UL, .tv_sec = 1};
624
- nanosleep(&tm, NULL);
625
- }
626
- return NULL;
627
- }
628
- static void iodine_start_io_thread(void *a1, void *a2) {
629
- (void)a1;
630
- (void)a2;
631
- pthread_create(&sock_io_pthread, NULL, iodine_io_thread, NULL);
632
- }
633
- static void iodine_join_io_thread(void) {
634
- sock_io_thread = 0;
635
- pthread_join(sock_io_pthread, NULL);
636
- }
637
-
638
- static void *srv_start_no_gvl(void *_) {
639
- (void)(_);
640
- // collect requested settings
641
- VALUE rb_th_i = rb_iv_get(Iodine, "@threads");
642
- VALUE rb_pr_i = rb_iv_get(Iodine, "@processes");
643
- ssize_t threads = (TYPE(rb_th_i) == T_FIXNUM) ? FIX2LONG(rb_th_i) : 0;
644
- ssize_t processes = (TYPE(rb_pr_i) == T_FIXNUM) ? FIX2LONG(rb_pr_i) : 0;
645
- // print a warnning if settings are sub optimal
646
- #ifdef _SC_NPROCESSORS_ONLN
647
- size_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
648
- if (processes <= 0)
649
- processes = 0;
650
- if (threads <= 0)
651
- threads = 0;
652
-
653
- if (processes && threads && cpu_count > 0 &&
654
- (((size_t)processes << 1) < cpu_count ||
655
- (size_t)processes > (cpu_count << 1)))
656
- fprintf(
657
- stderr,
658
- "* Performance warnning:\n"
659
- " - This computer has %lu CPUs available and you'll be "
660
- "utilizing %lu processes.\n - %s\n"
661
- " - Use the command line option: `-w %lu`\n"
662
- " - Or, within Ruby: `Iodine.processes = %lu`\n",
663
- cpu_count, (processes ? processes : 1),
664
- ((size_t)processes < cpu_count
665
- ? "Some CPUs won't be utilized, inhibiting performance."
666
- : "This causes excessive context switches, wasting resources."),
667
- cpu_count, cpu_count);
668
- #else
669
- if (processes <= 0)
670
- processes = 0;
671
- if (threads <= 0)
672
- threads = 0;
673
- #endif
674
- sock_io_thread = 1;
675
- defer(iodine_start_io_thread, NULL, NULL);
676
- facil_run(.threads = threads, .processes = processes,
677
- .on_finish = iodine_join_io_thread);
678
- return NULL;
679
- }
680
-
681
- /**
682
- Starts the Iodine event loop. This will hang the thread until an interrupt
683
- (`^C`) signal is received.
684
-
685
- Returns the Iodine module.
686
- */
687
- static VALUE iodine_start(VALUE self) {
688
- if (iodine_http_review() == -1) {
689
- perror("Iodine couldn't start HTTP service... port busy? ");
690
- return Qnil;
691
- }
692
- rb_thread_call_without_gvl2(srv_start_no_gvl, (void *)self, NULL, NULL);
693
-
694
- return self;
695
- }
696
-
697
- /* *****************************************************************************
698
- Initializing the library
699
- */
700
-
701
- ////////////////////////////////////////////////////////////////////////
702
- // Ruby loads the library and invokes the Init_<lib_name> function...
703
- //
704
- // Here we connect all the C code to the Ruby interface, completing the bridge
705
- // between Lib-Server and Ruby.
706
- void Init_iodine(void) {
707
- // initialize globally used IDs, for faster access to the Ruby layer.
708
- call_proc_id = rb_intern("call");
709
- new_func_id = rb_intern("new");
710
- on_start_func_id = rb_intern("on_start");
711
- on_finish_func_id = rb_intern("on_finish");
712
- on_open_func_id = rb_intern("on_open");
713
- on_message_func_id = rb_intern("on_message");
714
- on_data_func_id = rb_intern("on_data");
715
- on_shutdown_func_id = rb_intern("on_shutdown");
716
- on_close_func_id = rb_intern("on_close");
717
- on_ready_func_id = rb_intern("on_ready");
718
- ping_func_id = rb_intern("ping");
719
- buff_var_id = rb_intern("scrtbuffer");
720
- fd_var_id = rb_intern("scrtfd");
721
- timeout_var_id = rb_intern("@timeout");
722
- to_s_method_id = rb_intern("to_s");
723
-
724
- BinaryEncodingIndex = rb_enc_find_index("binary"); // sets encoding for data
725
- UTF8EncodingIndex = rb_enc_find_index("UTF-8"); // sets encoding for data
726
- BinaryEncoding = rb_enc_find("binary"); // sets encoding for data
727
- UTF8Encoding = rb_enc_find("UTF-8"); // sets encoding for data
728
-
729
- // The core Iodine module wraps libserver functionality and little more.
730
- Iodine = rb_define_module("Iodine");
731
-
732
- // get-set version
733
- {
734
- Iodine_Version = rb_const_get(Iodine, rb_intern("VERSION"));
735
- if (Iodine_Version == Qnil)
736
- Iodine_Version_Str = "0.2.0";
737
- else
738
- Iodine_Version_Str = StringValueCStr(Iodine_Version);
739
- }
740
- // the Iodine singleton functions
741
- rb_define_module_function(Iodine, "listen", iodine_listen_dyn_protocol, 2);
742
- rb_define_module_function(Iodine, "start", iodine_start, 0);
743
- rb_define_module_function(Iodine, "count", iodine_count, 0);
744
- rb_define_module_function(Iodine, "run", iodine_run_async, 0);
745
- rb_define_module_function(Iodine, "run_after", iodine_run_after, 1);
746
- rb_define_module_function(Iodine, "run_every", iodine_run_every, -1);
747
-
748
- // Every Protocol (and Server?) instance will hold a reference to the server
749
- // define the Server Ruby class.
750
- IodineBase = rb_define_module_under(Iodine, "Base");
751
-
752
- // Initialize the registry under the Iodine core
753
- Registry.init(Iodine);
754
- // Initialize the Dynamic Protocol
755
- Init_DynamicProtocol();
756
-
757
- // initialize the Http server
758
- // must be done only after all the globals, including BinaryEncoding, are set
759
- Init_iodine_http();
760
- }