iodine 0.7.9 → 0.7.10

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.

@@ -65,7 +65,7 @@ b = a.map {|s| s.length }
65
65
  puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
66
66
  "static uint8_t html_escape_len[] = {", b.to_s.slice(1..-2),"};"
67
67
  */
68
- static char *html_escape_strs[] = {
68
+ static const char *html_escape_strs[] = {
69
69
  "�", "", "", "", "", "", "",
70
70
  "", "", "	", "
", "", "", "
",
71
71
  "", "", "", "", "", "", "",
@@ -359,8 +359,8 @@ static void resp_on_sub_message(struct redis_engine_internal_s *i, FIOBJ msg) {
359
359
  if (FIOBJ_TYPE(msg) != FIOBJ_T_STRING || fiobj_obj2cstr(msg).len != 4 ||
360
360
  fiobj_obj2cstr(msg).data[0] != 'P') {
361
361
  FIO_LOG_WARNING("(redis) unexpected data format in "
362
- "subscription stream:");
363
- FIO_LOG_STATE(" %s\n", fiobj_obj2cstr(msg).data);
362
+ "subscription stream:\n %s",
363
+ fiobj_obj2cstr(msg).data);
364
364
  }
365
365
  } else {
366
366
  // FIOBJ *ary = fiobj_ary2ptr(msg);
@@ -840,10 +840,10 @@ FIO_IGNORE_MACRO(struct redis_engine_create_args args) {
840
840
  }
841
841
 
842
842
  if (!args.address.data || !args.address.len) {
843
- args.address = (fio_str_info_s){.len = 9, .data = "localhost"};
843
+ args.address = (fio_str_info_s){.len = 9, .data = (char *)"localhost"};
844
844
  }
845
845
  if (!args.port.data || !args.port.len) {
846
- args.port = (fio_str_info_s){.len = 4, .data = "6379"};
846
+ args.port = (fio_str_info_s){.len = 4, .data = (char *)"6379"};
847
847
  }
848
848
  redis_engine_s *r =
849
849
  fio_malloc(sizeof(*r) + args.port.len + 1 + args.address.len + 1 +
@@ -526,15 +526,6 @@ typedef struct {
526
526
  void *udata;
527
527
  } websocket_sub_data_s;
528
528
 
529
- static void websocket_on_unsubscribe(void *u1, void *u2) {
530
- websocket_sub_data_s *d = u2;
531
- if (d->on_unsubscribe) {
532
- d->on_unsubscribe(d->udata);
533
- }
534
- free(d);
535
- (void)u1;
536
- }
537
-
538
529
  static inline void websocket_on_pubsub_message_direct_internal(fio_msg_s *msg,
539
530
  uint8_t txt) {
540
531
  fio_protocol_s *pr =
@@ -563,7 +554,6 @@ static inline void websocket_on_pubsub_message_direct_internal(fio_msg_s *msg,
563
554
  break;
564
555
  }
565
556
  if (pre_wrapped) {
566
- // fprintf(stderr, "INFO: WebSocket Pub/Sub optimized for broadcast\n");
567
557
  fiobj_send_free((intptr_t)msg->udata1, fiobj_dup(pre_wrapped));
568
558
  goto finish;
569
559
  }
@@ -607,6 +597,25 @@ static void websocket_on_pubsub_message(fio_msg_s *msg) {
607
597
  fio_protocol_unlock(pr, FIO_PR_LOCK_TASK);
608
598
  }
609
599
 
600
+ static void websocket_on_unsubscribe(void *u1, void *u2) {
601
+ websocket_sub_data_s *d = u2;
602
+ if (d->on_unsubscribe) {
603
+ d->on_unsubscribe(d->udata);
604
+ }
605
+
606
+ if ((intptr_t)d->on_message == (intptr_t)WEBSOCKET_OPTIMIZE_PUBSUB) {
607
+ websocket_optimize4broadcasts(WEBSOCKET_OPTIMIZE_PUBSUB, 0);
608
+ } else if ((intptr_t)d->on_message ==
609
+ (intptr_t)WEBSOCKET_OPTIMIZE_PUBSUB_TEXT) {
610
+ websocket_optimize4broadcasts(WEBSOCKET_OPTIMIZE_PUBSUB_TEXT, 0);
611
+ } else if ((intptr_t)d->on_message ==
612
+ (intptr_t)WEBSOCKET_OPTIMIZE_PUBSUB_BINARY) {
613
+ websocket_optimize4broadcasts(WEBSOCKET_OPTIMIZE_PUBSUB_BINARY, 0);
614
+ }
615
+ free(d);
616
+ (void)u1;
617
+ }
618
+
610
619
  /**
611
620
  * Returns a subscription ID on success and 0 on failure.
612
621
  */
@@ -621,18 +630,28 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
621
630
  .on_message = args.on_message,
622
631
  .on_unsubscribe = args.on_unsubscribe,
623
632
  };
624
- subscription_s *sub = fio_subscribe(
625
- .channel = args.channel, .match = args.match,
626
- .on_unsubscribe = websocket_on_unsubscribe,
627
- .on_message =
628
- (args.on_message
629
- ? websocket_on_pubsub_message
630
- : args.force_binary
631
- ? websocket_on_pubsub_message_direct_bin
632
- : args.force_text
633
- ? websocket_on_pubsub_message_direct_txt
634
- : websocket_on_pubsub_message_direct),
635
- .udata1 = (void *)args.ws->fd, .udata2 = d);
633
+ void (*handler)(fio_msg_s *) = websocket_on_pubsub_message;
634
+ if (!args.on_message) {
635
+ intptr_t br_type;
636
+ if (args.force_binary) {
637
+ br_type = WEBSOCKET_OPTIMIZE_PUBSUB_BINARY;
638
+ handler = websocket_on_pubsub_message_direct_bin;
639
+ } else if (args.force_text) {
640
+ br_type = WEBSOCKET_OPTIMIZE_PUBSUB_TEXT;
641
+ handler = websocket_on_pubsub_message_direct_txt;
642
+ } else {
643
+ br_type = WEBSOCKET_OPTIMIZE_PUBSUB;
644
+ handler = websocket_on_pubsub_message_direct;
645
+ }
646
+ websocket_optimize4broadcasts(br_type, 1);
647
+ d->on_message =
648
+ (void (*)(ws_s *, fio_str_info_s, fio_str_info_s, void *))br_type;
649
+ }
650
+ subscription_s *sub =
651
+ fio_subscribe(.channel = args.channel, .match = args.match,
652
+ .on_unsubscribe = websocket_on_unsubscribe,
653
+ .on_message = handler, .udata1 = (void *)args.ws->fd,
654
+ .udata2 = d);
636
655
  if (!sub) {
637
656
  /* don't free `d`, return (`d` freed by fio_subscribe) */
638
657
  return 0;
@@ -152,19 +152,11 @@ void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id);
152
152
  /**
153
153
  * Enables (or disables) broadcast optimizations.
154
154
  *
155
- * When using WebSocket pub/sub system is originally optimized for either
156
- * non-direct transmission (messages are handled by callbacks) or direct
157
- * transmission to 1-3 clients per channel (on average), meaning that the
158
- * majority of the messages are meant for a single recipient (or multiple
159
- * callback recipients) and only some are expected to be directly transmitted to
160
- * a group.
155
+ * This is performed automatically by the `websocket_subscribe` function.
156
+ * However, this function is provided for enabling the pub/sub metadata based
157
+ * optimizations for external connections / subscriptions.
161
158
  *
162
- * However, when most messages are intended for direct transmission to more than
163
- * 3 clients (on average), certain optimizations can be made to improve memory
164
- * consumption (minimize duplication or WebSocket network data).
165
- *
166
- * This function allows enablement (or disablement) of these optimizations.
167
- * These optimizations include:
159
+ * This function allows enablement (or disablement) of these optimizations:
168
160
  *
169
161
  * * WEBSOCKET_OPTIMIZE_PUBSUB - optimize all direct transmission messages,
170
162
  * best attempt to detect Text vs. Binary data.
@@ -174,6 +166,15 @@ void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id);
174
166
  * Note: to disable an optimization it should be disabled the same amount of
175
167
  * times it was enabled - multiple optimization enablements for the same type
176
168
  * are merged, but reference counted (disabled when reference is zero).
169
+ *
170
+ * Note2: The pub/sub metadata type ID will match the optimnization type
171
+ * requested (i.e., `WEBSOCKET_OPTIMIZE_PUBSUB`) and the optimized data is a
172
+ * FIOBJ String containing a pre-encoded WebSocket packet ready to be sent.
173
+ * i.e.:
174
+ *
175
+ * FIOBJ pre_wrapped = (FIOBJ)fio_message_metadata(msg,
176
+ * WEBSOCKET_OPTIMIZE_PUBSUB);
177
+ * fiobj_send_free((intptr_t)msg->udata1, fiobj_dup(pre_wrapped));
177
178
  */
178
179
  void websocket_optimize4broadcasts(intptr_t type, int enable);
179
180
 
@@ -31,8 +31,6 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.required_ruby_version = '>= 2.2.2' # Because earlier versions had been discontinued
33
33
 
34
- spec.add_dependency 'rack', '>= 1.0', '< 3.0'
35
-
36
34
  spec.requirements << 'A Unix based system: Linux / macOS / BSD.'
37
35
  spec.requirements << 'An updated C compiler.'
38
36
  spec.requirements << 'Ruby >= 2.2.2 required for Rack 2.'
@@ -63,6 +63,10 @@ module Iodine
63
63
  #
64
64
  # This is recommended, see {Iodine::Rack::Utils} for details.
65
65
  def self.patch_rack
66
+ begin
67
+ require 'rack'
68
+ rescue LoadError => e
69
+ end
66
70
  ::Rack::Utils.class_eval do
67
71
  Iodine::Base::MonkeyPatch::RackUtils.methods(false).each do |m|
68
72
  ::Rack::Utils.define_singleton_method(m,
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.9'.freeze
2
+ VERSION = '0.7.10'.freeze
3
3
  end
@@ -24,13 +24,11 @@ ENV['RACK_HANDLER'] = 'iodine'
24
24
  begin
25
25
  require 'rack/handler' unless defined?(Rack::Handler)
26
26
  Rack::Handler::WEBrick = ::Iodine::Rack # Rack::Handler.get(:iodine)
27
- rescue Exception
28
-
27
+ rescue LoadError
29
28
  end
30
29
 
31
30
  begin
32
31
  ::Rack::Handler.register('iodine', 'Iodine::Rack') if defined?(::Rack::Handler)
33
32
  ::Rack::Handler.register('Iodine', 'Iodine::Rack') if defined?(::Rack::Handler)
34
- rescue Exception
35
-
33
+ rescue StandardError
36
34
  end
metadata CHANGED
@@ -1,35 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.9
4
+ version: 0.7.10
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-11-15 00:00:00.000000000 Z
11
+ date: 2018-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rack
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '3.0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: '1.0'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '3.0'
33
13
  - !ruby/object:Gem::Dependency
34
14
  name: bundler
35
15
  requirement: !ruby/object:Gem::Requirement
@@ -131,34 +111,20 @@ files:
131
111
  - Rakefile
132
112
  - SPEC-PubSub-Draft.md
133
113
  - SPEC-Websocket-Draft.md
134
- - bin/config.ru
135
114
  - bin/console
136
- - bin/echo
137
- - bin/http-big
138
- - bin/http-hello
139
- - bin/http-playground
140
- - bin/mustache.rb
141
- - bin/playground
115
+ - bin/info.md
116
+ - bin/mustache_bench.rb
142
117
  - bin/poc/Gemfile.lock
143
118
  - bin/poc/README.md
144
119
  - bin/poc/config.ru
145
120
  - bin/poc/gemfile
146
121
  - bin/poc/www/index.html
147
- - bin/raw-rbhttp
148
- - bin/raw-rbhttp-em
149
- - bin/raw_broadcast
150
- - bin/test_with_faye
151
- - bin/updated api
152
- - bin/ws-broadcast
153
- - bin/ws-echo
154
122
  - examples/config.ru
155
123
  - examples/echo.ru
156
124
  - examples/hello.ru
157
- - examples/info.md
158
125
  - examples/pubsub_engine.ru
159
126
  - examples/redis.ru
160
127
  - examples/shootout.ru
161
- - examples/test_template.mustache
162
128
  - exe/iodine
163
129
  - ext/iodine/extconf.rb
164
130
  - ext/iodine/fio.c
@@ -244,7 +210,7 @@ licenses:
244
210
  - MIT
245
211
  metadata:
246
212
  allowed_push_host: https://rubygems.org
247
- post_install_message: 'Thank you for installing Iodine 0.7.9.
213
+ post_install_message: 'Thank you for installing Iodine 0.7.10.
248
214
 
249
215
  '
250
216
  rdoc_options: []
@@ -1,97 +0,0 @@
1
- require 'benchmark'
2
- require 'rack/sendfile'
3
- require 'rack/lint'
4
-
5
- # There are a number of possible applications to run in this file,
6
- # because I use it to test stuff.
7
- #
8
- # This value (app) sets which of the different applications will run.
9
- #
10
- # Valid values are "hello", "slow" (debugs env values), "simple"
11
- app = 'hello'
12
-
13
- # This is a simple Hello World Rack application, for benchmarking.
14
- HELLO_RESPONSE = [200, { 'Content-Type'.freeze => 'text/html'.freeze,
15
- 'Content-Length'.freeze => '16'.freeze }.freeze,
16
- ['Hello from Rack!'.freeze]].freeze
17
-
18
- hello = proc do |_env|
19
- HELLO_RESPONSE
20
- end
21
-
22
- slow = proc do |env|
23
- request = Rack::Request.new(env)
24
- # Benchmark.bm do |bm|
25
- # bm.report('Reading from env Hash to a string X 1000') { 1000.times { out = "ENV:\r\n#{env.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n"}\n" } }
26
- # bm.report('Creating the Rack::Request (can\'t repeat)') { 1.times { request = Rack::Request.new(env) } }
27
- # end
28
- if request.path_info == '/source'.freeze
29
- [200, { 'X-Sendfile' => File.expand_path(__FILE__) }, []]
30
- elsif request.path_info == '/file'.freeze
31
- [200, { 'X-Header' => 'This was a Rack::Sendfile response' }, File.open(__FILE__)]
32
- else
33
- out = "ENV:\n<br/>\n#{env.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n<br/>\n"}\n<br/>\n"
34
- out += "\n<br/>\nRequest Path: #{request.path_info}\n<br/>\nParams:\n<br/>\n#{request.params.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n<br/>\n"}\n<br/>\n" unless request.params.empty?
35
- [200, { 'Content-Type'.freeze => 'text/html'.freeze,
36
- 'Content-Length'.freeze => out.length.to_s },
37
- [out]]
38
- end
39
- end
40
-
41
- simple = proc do |env|
42
- request = Rack::Request.new(env)
43
- if request.path_info == '/source'.freeze
44
- [200, { 'X-Sendfile' => File.expand_path(__FILE__) }, []]
45
- elsif request.path_info == '/file'.freeze
46
- [200, { 'X-Header' => 'This was a Rack::Sendfile response' }, File.open(__FILE__)]
47
- else
48
- [200, { 'Content-Type'.freeze => 'text/html'.freeze,
49
- 'Content-Length'.freeze => request.path_info.length.to_s },
50
- [request.path_info]]
51
- end
52
- end
53
-
54
- logo_png = nil
55
-
56
- big = proc do |_env|
57
- logo_png ||= IO.binread '../logo.png'
58
- [200, { 'Content-Length'.freeze => logo_png.length.to_s , 'Content-Type'.freeze => 'image/png'.freeze}, [logo_png]]
59
- end
60
-
61
- bigX = proc do |_env|
62
- logo_png ||= IO.binread '../logo.png'
63
- [200, { 'Content-Length'.freeze => logo_png.length.to_s , 'Content-Type'.freeze => 'image/png'.freeze, 'X-Sendfile'.freeze => '../logo.png'.freeze}, [logo_png]]
64
- end
65
-
66
- case app
67
- when 'simple'
68
- use Rack::Sendfile
69
- run simple
70
- when 'hello'
71
- run hello
72
- when 'big'
73
- run big
74
- when 'bigX'
75
- run bigX
76
- when 'slow'
77
- use Rack::Lint
78
- run slow
79
- else
80
- run hello
81
- end
82
-
83
- # ab -n 1000000 -c 2000 -k http://127.0.0.1:3000/
84
- # wrk -c400 -d5 -t12 http://localhost:3000/
85
- #
86
- # ab -n 1000000 -c 2000 -r http://127.0.0.1:3000/
87
- # wrk -c400 -d5 -t12 -H"Connection: close" http://localhost:3000/
88
- #
89
- # def cycle
90
- # puts `wrk -c4000 -d5 -t12 http://localhost:3000/`
91
- # sleep(2)
92
- # puts `wrk -c4000 -d5 -t12 http://localhost:3000/source`
93
- # sleep(3)
94
- # puts `wrk -c200 -d5 -t12 http://localhost:3000/file`
95
- # true
96
- # end
97
- # sleep(10) while cycle
data/bin/echo DELETED
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # this will compile Iodine and run an echo server.
4
-
5
- # # test using:
6
- # telnet localhost 3000
7
-
8
- Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
9
- puts `rake clean`
10
- puts `rake compile`
11
-
12
- require 'benchmark'
13
- $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
14
- require 'bundler/setup'
15
- require 'iodine'
16
-
17
- class EchoProtocol
18
- @timeout = 10
19
-
20
- def self.on_start
21
- puts '* Echo service is now running.'
22
- end
23
-
24
- def on_open
25
- puts 'New connection'
26
- end
27
-
28
- def ping
29
- write "-- are you there?\n"
30
- end
31
-
32
- # `on_message` is an optional alternative to the `on_data` callback.
33
- # `on_message` has a 1Kb buffer that recycles itself for memory optimization.
34
- def on_message(buffer)
35
- # writing will never block and will use a buffer written in C when needed.
36
- write! "Echo: #{buffer}"
37
- puts buffer.dump
38
- close if buffer =~ /^bye[\r\n]/i
39
- end
40
- end
41
-
42
- # create the server object and setup any settings we might need.
43
- Iodine.listen 3000, EchoProtocol
44
- Iodine.threads = 10
45
- # server.processes = 1
46
- Iodine.start
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # this will compile Iodine and run an HTTP server "Hello World" example.
4
- Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
5
- puts `rake clean`
6
- puts `rake compile`
7
-
8
- require 'benchmark'
9
- $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
10
- require 'bundler/setup'
11
- require 'iodine'
12
- require 'rack'
13
-
14
- # create the server object and setup any settings we might need.
15
- Iodine::Rack
16
- Iodine.threads ||= 1
17
- Iodine.processes ||= 1 # 4
18
- Iodine::Rack.public = '~/Documents/Scratch'
19
- count = 2
20
- logo_png = IO.binread './logo.png'
21
- Iodine::Rack.app = proc { |_env| [200, { 'Content-Length'.freeze => logo_png.length.to_s , 'Content-Type'.freeze => 'image/png'.freeze}, [logo_png]] }
22
- puts Iodine::Rack.address
23
- Iodine.start
24
-
25
- # puts env.to_a.map { |pair| pair.join(': ') } .join("\n").to_s;
26
-
27
- # puts "Press enter to start (#{Process.pid})"
28
- # gets
29
-
30
- ###############
31
- ## for testing:
32
-
33
- # def nag
34
- # puts `ab -n 200000 -c 2000 -k http://127.0.0.1:3000/`
35
- # sleep 2
36
- # end
37
- #
38
- # nag while true
39
- #
40
- # def nag
41
- # puts `wrk -c2000 -d10 -t4 http://localhost:3000/`
42
- # sleep 3
43
- # end
44
- #
45
- # nag while true
46
-
47
- # ab -n 100000 -c 200 -k http://127.0.0.1:3000/
48
- # ab -n 100000 -c 4000 -k http://127.0.0.1:3000/
49
- # ab -n 1000000 -c 20000 -k http://127.0.0.1:3000/
50
- # ~/ruby/wrk/wrk -c400 -d10 -t12 http://localhost:3000/
51
- # wrk -c200 -d4 -t12 http://localhost:3000/
52
- # RACK_ENV="production" rackup -p 3000 -s iodine
53
-
54
- # thor --amount 5000 ws://localhost:3000/echo
55
- # thor --amount 5000 ws://localhost:3000/broadcast
56
-
57
- # ws = new WebSocket("ws://localhost:3000"); ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data);}; ws.onclose = function(e) {console.log("closed")}; ws.onopen = function(e) {ws.send("hi");};
58
- # for(i = 0; i< 256; i++) {
59
- # ws = new WebSocket("ws://localhost:3000");
60
- # ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data); e.target.close(); };
61
- # ws.onclose = function(e) {console.log("closed")};
62
- # ws.onopen = function(e) {e.target.send("hi");};
63
- # };