iodine 0.1.21 → 0.2.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
data/lib/iodine.rb CHANGED
@@ -1,71 +1,70 @@
1
- require 'logger'
2
1
  require 'socket'
3
- require 'openssl'
4
2
 
5
- require "iodine/version"
6
- require "iodine/settings"
7
- require "iodine/logging"
8
- require "iodine/core"
9
- require "iodine/timers"
10
- require "iodine/protocol"
11
- require "iodine/ssl_connector"
12
- require "iodine/io"
13
- require "iodine/core_init"
3
+ require 'iodine/version'
4
+ require 'iodine/iodine'
14
5
 
15
-
16
-
17
- # Iodine is an easy Object-Oriented library for writing network applications (servers) with your own
18
- # network protocol.
19
- #
20
- # Please read the {file:README.md} file for an introduction to Iodine.
6
+ # Iodine is both a Rack server and a platform for writing evented network services on Ruby.
21
7
  #
22
- # Here's a quick and easy echo server,
23
- # notice how Iodine will automatically start running once you finish setting everything up:
8
+ # Here is a sample Echo server using Iodine:
24
9
  #
25
- # require 'iodine'
26
10
  #
27
- # class MyProtocol < Iodine::Protocol
28
- # # Iodine will call this whenever a new connection is opened.
29
- # def on_open
30
- # # Iodine includes logging as well as unique assigned instance ID's.
31
- # Iodine.info "New connection id: #{id}"
32
- # # Iodine includes timeout support with automatic pinging or connection termination.
33
- # set_timeout 5
34
- # end
35
- # def on_message data
36
- # write("-- Closing connection, goodbye.\n") && close if data =~ /^(bye|close|exit)/i
37
- # write(">> #{data.chomp}\n")
38
- # end
39
- # # Iodine will call this whenever a new connection is closed.
40
- # def on_close
41
- # Iodine.info "Closing connection id: #{id}"
42
- # end
43
- # # Iodine will call this whenever a a timeout is reached.
44
- # def ping
45
- # # If `write` fails, it automatically closes the connection.
46
- # write("-- Are you still there?\n")
47
- # end
11
+ # # define the protocol for our service
12
+ # class EchoProtocol
13
+ # @timeout = 10
14
+ # # this is just one possible callback with a recyclable buffer
15
+ # def on_message buffer
16
+ # # write the data we received
17
+ # write "echo: #{buffer}"
18
+ # # close the connection when the time comes
19
+ # close if buffer =~ /^bye[\n\r]/
20
+ # end
48
21
  # end
22
+ # # create the service instance
23
+ # Iodine.listen 3000, EchoProtocol
24
+ # # start the service
25
+ # Iodine.start
49
26
  #
50
- # # setting up the server is as easy as plugging in your Protocol class:
51
- # Iodine.protocol = MyProtocol
52
- #
53
- # # setting up cuncurrency can be done with threads - the default is a single thread):
54
- # Iodine.threads = 8
55
- #
56
- # # setting up cuncurrency can also be done with processes (forking) - the default is a single process:
57
- # Iodine.processes = 4
58
- #
59
- # # if you are excecuting this script from IRB, exit IRB to start Iodine.
60
- # exit
61
- #
62
- # You can test the example using `telnet`
63
27
  #
64
- # Before you write your own protocol, make sure you learn more about the {Iodine::Protocol} and all it's got to offer.
28
+ # Please read the {file:README.md} file for an introduction to Iodine and an overview of it's API.
65
29
  module Iodine
66
- extend self
67
- end
30
+ @threads = (ARGV.index('-t') && ARGV[ARGV.index('-t') + 1]) || ENV['MAX_THREADS']
31
+ @processes = (ARGV.index('-w') && ARGV[ARGV.index('-w') + 1]) || ENV['MAX_WORKERS']
32
+ @threads = @threads.to_i if @threads
33
+ @processes = @processes.to_i if @processes
34
+
35
+ # Get/Set the number of threads used in the thread pool (a static thread pool). Can be 1 (single working thread, the main thread will sleep) and can be 0 (the main thread will be used as the only active thread).
36
+ def self.threads
37
+ @threads
38
+ end
39
+
40
+ # Get/Set the number of threads used in the thread pool (a static thread pool). Can be 1 (single working thread, the main thread will sleep) and can be 0 (the main thread will be used as the only active thread).
41
+ def self.threads=(count)
42
+ @threads = count.to_i
43
+ end
68
44
 
45
+ # Get/Set the number of worker processes. A value greater then 1 will cause the Iodine to "fork" any extra worker processes needed.
46
+ def self.processes
47
+ @processes
48
+ end
69
49
 
50
+ # Get/Set the number of worker processes. A value greater then 1 will cause the Iodine to "fork" any extra worker processes needed.
51
+ def self.processes=(count)
52
+ @processes = count.to_i
53
+ end
54
+
55
+ # Runs the warmup sequence. {warmup} attempts to get every "autoloaded" (lazy loaded)
56
+ # file to load now instead of waiting for "first access". This allows multi-threaded safety and better memory utilization during forking.
57
+ #
58
+ # Use {warmup} when either {processes} or {threads} are set to more then 1.
59
+ def self.warmup
60
+ # load anything marked with `autoload`, since autoload isn't thread safe nor fork friendly.
61
+ Module.constants.each do |n|
62
+ begin
63
+ Object.const_get(n)
64
+ rescue Exception => _e
65
+ end
66
+ end
67
+ end
68
+ end
70
69
 
71
- # require 'iodine/http'
70
+ require 'rack/handler/iodine' unless defined? ::Iodine::Rack::IODINE_RACK_LOADED
data/lib/iodine/http.rb CHANGED
@@ -1,193 +1,4 @@
1
1
  require 'iodine'
2
- require 'stringio'
3
- require 'time'
4
- require 'json'
5
- require 'yaml'
6
- require 'uri'
7
- require 'tmpdir'
8
- require 'zlib'
9
- require 'securerandom'
10
- require 'tempfile.rb'
11
-
12
- require 'iodine/http/request'
13
- require 'iodine/http/response'
14
- require 'iodine/http/session'
15
-
16
- require 'iodine/http/http1'
17
-
18
- require 'iodine/http/hpack'
19
- require 'iodine/http/http2'
20
-
21
- require 'iodine/http/websockets'
22
- require 'iodine/http/websocket_handler'
23
- require 'iodine/http/websocket_client'
24
-
25
- require 'iodine/http/rack_support'
26
-
27
2
 
28
3
  module Iodine
29
-
30
- # The {Iodine::Http} class allows the creation of Http and Websocket servers using Iodine.
31
- #
32
- # To start an Http server, simply require `iodine/http` (which isn't required by default) and set up
33
- # your Http callback. i.e.:
34
- #
35
- # require 'iodine/http'
36
- # Iodine::Http.on_http { |request, response| 'Hello World!' }
37
- # exit # only if running from irb
38
- #
39
- # To start a Websocket server, require `iodine/http` (which isn't required by default), create a Websocket handling Class and set up
40
- # your Websocket callback. i.e.:
41
- #
42
- # require 'iodine/http'
43
- # class WSChatServer
44
- # def initialize nickname, response
45
- # @nickname = nickname || "unknown"
46
- # @response = response
47
- # # @response.io # => Http Protocol
48
- # end
49
- # def on_open
50
- # # only now is the response.io pointing at the Websocket Protocol
51
- # @io = @response.io
52
- # @io.broadcast "#{@nickname} has joined the chat!"
53
- # @io << "Welcome #{@nickname}, you have joined the chat!"
54
- # end
55
- # def on_message data
56
- # @io.broadcast "#{@nickname} >> #{data}"
57
- # @io << ">> #{data}"
58
- # end
59
- # def on_broadcast data
60
- # # the http response can also be used to send websocket data.
61
- # @response << data
62
- # end
63
- # def on_close
64
- # @io.broadcast "#{@nickname} has left the chat!"
65
- # end
66
- # end
67
- #
68
- # Iodine::Http.on_websocket { |request, response| WSChatServer.new request.params[:name], response}
69
- #
70
- # See {Iodine::Http::WebsocketHandler} for a good starting point or inherit {Iodine::Http::WebsocketHandler} in your handler.
71
- #
72
- module Http
73
- public
74
- # Sets or gets the Http callback.
75
- #
76
- # An Http callback is a Proc like object that answers to `call(request, response)` and returns either:
77
- # `true`:: the response has been set by the callback and can be managed (including any streaming) by the server.
78
- # `false`:: the request shouldn't be answered or resource not found (error 404 will be sent as a response).
79
- # String:: the String will be appended to the response and the response sent.
80
- def on_http handler = nil, &block
81
- @http_app = handler || block if handler || block
82
- @http_app
83
- end
84
- # Sets or gets the Websockets callback.
85
- #
86
- # A Websockets callback is a Proc like object that answers to `call(request)` and returns either:
87
- # `false`:: the request shouldn't be answered or resource not found (error 404 will be sent as a response).
88
- # Websocket Handler:: a Websocket handler is an object that is expected to answer `on_message(data)` and `on_close`. See {} for more data.
89
- def on_websocket handler = nil, &block
90
- @websocket_app = handler || block if handler || block
91
- @websocket_app
92
- end
93
-
94
- # Sets the session token for the Http server (String). Defaults to the name of the script + '_id'.
95
- def session_token= token
96
- @session_token = token
97
- end
98
- # Gets the session token for the Http server (String). Defaults to the name of the script.
99
- def session_token
100
- @session_token
101
- end
102
-
103
- # `max_http_buffer` is deprecated. Use `max_body_size` instead.
104
- def max_http_buffer= size
105
- Iodine.warn "`max_http_buffer` is deprecated. Use `max_body_size` instead."
106
- max_body_size = size
107
- end
108
- # `max_http_buffer` is deprecated. Use `max_body_size` instead.
109
- def max_http_buffer
110
- Iodine.warn "`max_http_buffer` is deprecated. Use `max_body_size` instead."
111
- @max_body_size
112
- end
113
- # Sets the maximum bytes allowed for an HTTP's body request (file upload data). Defaults to the command line `-limit` argument, or ~0.5GB.
114
- def max_body_size= size
115
- @max_body_size = size
116
- end
117
- # Gets the maximum bytes allowed for an HTTP's body request (file upload data). Defaults to the command line `-limit` argument, or ~0.5GB.
118
- def max_body_size
119
- @max_body_size
120
- end
121
-
122
- # Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
123
- def http2= allow
124
- @http2 = allow && true
125
- end
126
- # Returns true if Iodine will require that new connection be encrypted.
127
- def http2
128
- @http2
129
- end
130
-
131
- # # Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
132
- # def self.message_buffer_size= size
133
- # @message_buffer_size = size
134
- # end
135
- # # Returns true if Iodine will require that new connection be encrypted.
136
- # def self.message_buffer_size
137
- # @message_buffer_size
138
- # end
139
-
140
- # Creates a websocket client within a new task (non-blocking).
141
- #
142
- # Make sure to setup all the callbacks (as needed) prior to starting the connection. See {::Iodine::Http::WebsocketClient.connect}
143
- #
144
- # i.e.:
145
- #
146
- # require 'iodine/http'
147
- # # don't start the server
148
- # Iodine.protocol = :timer
149
- # options = {}
150
- # options[:on_open] = Proc.new { write "Hello there!"}
151
- # options[:on_message] = Proc.new do |data|
152
- # puts ">> #{data}";
153
- # write "Bye!";
154
- # # It's possible to update the callback midstream.
155
- # on_message {|data| puts "-- Goodbye message: #{data}"; close;}
156
- # end
157
- # # After closing we will call `Iodine.signal_exit` to signal Iodine to finish up.
158
- # options[:on_close] = Proc.new { puts "disconnected"; Iodine.signal_exit }
159
- #
160
- # Iodine::Http.ws_connect "ws://echo.websocket.org", options
161
- #
162
- # #if running from irb:
163
- # exit
164
- #
165
- def ws_connect url, options={}, &block
166
- ::Iodine.run { ::Iodine::Http::WebsocketClient.connect url, options, &block }
167
- end
168
-
169
- protected
170
-
171
- @http2 = (ARGV.index('http2') && true)
172
- @max_body_size = ((ARGV.index('-limit') && ARGV[ARGV.index('-limit') + 1]) || 536_870_912).to_i
173
-
174
- @websocket_app = @http_app = NOT_IMPLEMENTED = Proc.new { |i,o| false }
175
- @session_token = "#{File.basename($0, '.*')}_uuid"
176
- extend self
177
- end
178
-
179
- @queue.tap do |q|
180
- arr =[];
181
- arr << q.pop until q.empty?;
182
- run { ::Iodine.ssl_protocols = { 'h2' => ::Iodine::Http::Http2, 'http/1.1' => ::Iodine::Http::Http1 } if @ssl && @ssl_protocols.empty? && ::Iodine::Http.http2 }
183
- run do
184
- if Iodine.protocol == ::Iodine::Http::Http1 && ::Iodine::Http.on_http == ::Iodine::Http::NOT_IMPLEMENTED && ::Iodine::Http.on_websocket == ::Iodine::Http::NOT_IMPLEMENTED
185
- ::Iodine.protocol = :http_not_initialized
186
- q << arr.shift until arr.empty?
187
- run { Process.kill("INT", 0) }
188
- end
189
- end
190
- q << arr.shift until arr.empty?
191
- end
192
4
  end
193
- Iodine.protocol = ::Iodine::Http::Http1 unless Iodine.protocol
@@ -1,247 +1,38 @@
1
1
  module Iodine
2
-
3
- # This is the Basic Iodine server unit - a network protocol.
4
- #
5
- # A new protocol instance will be created for every network connection.
6
- #
7
- # A new protocol might be initialized also when switching between protocols. In this use-case, the
8
- # protocol can be initialized with an optional second `options` parameter (the first parameter MUST be the IO object used),
9
- # allowing this data to be accessed within the {#on_open} method using the `@options` instance variable or the `options` accessor.
10
- #
11
- # For example, when switching protocols midstream (i.e. for implementing an Http Upgrade to another protocol such as Websockets):
12
- #
13
- # class MyNextProtocol
14
- # def on_open
15
- # @secret = options[:secret]
16
- # end
17
- # end
18
- #
19
- # # in the old protocol:
20
- #
21
- # class MyOriginalProtocol
22
- # def switch_to_next_protocol
23
- # MyNextProtocol.new @io, secret: "my secret data"
24
- # end
25
- # end
26
- #
27
- # The recommended use is to inherit this class and override any of the following:
28
- # on_open:: called whenever the Protocol is initialized. Override this to initialize the Protocol object.
29
- # on_message(data):: called whenever data is received from the IO. Override this to implement the actual network protocol.
30
- # on_close:: called AFTER the Protocol's IO is closed.
31
- # on_shutdown:: called when the server's shutdown process had started and BEFORE the Protocol's IO is closed. It allows graceful shutdown for network protocols.
32
- # ping:: called when timeout was reached. see {#set_timeout}
33
- #
34
- # Once the network protocol class was created, remember to tell Iodine about it:
35
- # class MyProtocol << Iodine::Protocol
36
- # # your code here
37
- # end
38
- # # tell Iodine
39
- # Iodine.protocol = MyProtocol
40
- #
41
- class Protocol
42
-
43
- # returns the IO object. If the connection uses SSL/TLS, this will return the SSLSocket (not a native IO object).
44
- #
45
- # Using one of the Protocol methods {#write}, {#read}, {#close} is prefered over direct access.
46
- attr_reader :io
47
- # the argument or options Hash passed to the initializer as a second argument (the first argument MUST be the IO object).
48
- # the value is usually `nil` unless the protocol instance was created by a different protocol while "upgrading" from one protocol to the next.
49
- attr_reader :options
50
- # The protocol's Mutex locker. It should be locked whenever your code is runing, unless you are
51
- # writing asynchronous code.
52
- #
53
- # Use with care (or, better yet, don't use).
54
- attr_reader :locker
55
-
56
- # Sets the timeout in seconds for IO activity (set timeout within {#on_open}).
57
- #
58
- # After timeout is reached, {#ping} will be called. The connection will be closed if {#ping} returns `false` or `nil`.
59
- def set_timeout seconds
60
- @timeout = seconds
61
- end
62
-
63
- # This method is called whenever the Protocol is initialized - i.e.:
64
- # a new connection is established or an old connection switches to this protocol.
65
- def on_open
66
- end
67
- # This method is called whenever data is received from the IO.
68
- #
69
- # The `data` received is a reference to the socket's buffer and it will be cleared and replaced whenever `read`
70
- # is called or when new IO data comes in (after `on_message` had completed). To presist
71
- # the data, make sure to duplicate the data String using `data.dup`.
72
- def on_message data
73
- end
74
-
75
- # This method is called AFTER the Protocol's IO is closed - it will only be called once.
76
- def on_close
77
- end
78
-
79
- # This method is called when the server's shutdown process had started and BEFORE the Protocol's IO is closed. It allows graceful shutdown for network protocols.
80
- def on_shutdown
81
- end
82
-
83
- # This method is called whenever a timeout has occurred. Either implement a ping or close the connection.
84
- # The default implementation closes the connection unless the protocol is still processing information received before timeout occurred.
85
- def ping
86
- close unless @locker.locked?
87
- end
88
-
89
- #############
90
- ## functionality and helpers
91
-
92
-
93
- # returns true if the protocol is using an encrypted connection (the IO is an OpenSSL::SSL::SSLSocket).
94
- def ssl?
95
- @io.is_a?(OpenSSL::SSL::SSLSocket) # io.npn_protocol
96
- end
97
-
98
-
99
- # Closes the IO object.
100
- # @return [nil]
101
- def close
102
- @io.close unless @io.closed?
103
- nil
104
- end
105
- alias :disconnect :close
106
- def closed?
107
- @io.closed?
108
- end
109
-
110
- # reads from the IO up to the specified number of bytes (defaults to ~2Mb).
111
- def read size = 2_097_152
112
- touch
113
- ssl? ? read_ssl(size) : @io.read_nonblock( size , Thread.current[:buffer] )
114
- # @io.read_nonblock( size ) # this one is a bit slower...
115
- rescue OpenSSL::SSL::SSLErrorWaitReadable, IO::WaitReadable, IO::WaitWritable
116
- nil
117
- rescue IOError, Errno::ECONNRESET
118
- close
119
- rescue => e
120
- Iodine.warn "Protocol read error: #{e.class.name} #{e.message} (closing connection)"
121
- close
122
- end
123
-
124
- # this method, writes data to the socket / io object.
125
- def write data
126
- begin
127
- @send_locker.synchronize do
128
- r = @io.write data
129
- touch
130
- r
131
- end
132
- rescue # => e
133
- # Iodine.info e.message
134
- close
135
- end
136
- end
137
-
138
- # returns the connection's unique local ID as a Hex string.
139
- #
140
- # This can be used locally but not across processes.
141
- def id
142
- @id ||= @io.to_io.object_id.to_s(16)
143
- end
144
-
145
- # @return [Enumerable] returns an Enumerable with all the active connections (instances of THIS Protocol or it's children).
146
- #
147
- # if a block is passed, than this method exceutes the block.
148
- def self.each
149
- if block_given?
150
- Iodine.to_a.each {|p| yield(p) if p.is_a?(self) }
151
- else
152
- ( Iodine.to_a.select {|p| p.is_a?(self) } ).each
153
- end
154
- end
155
-
156
-
157
- #################
158
- ## the following are Iodine's "system" methods, used internally. Don't override.
159
-
160
-
161
- # This method is used by Iodine to initialized the Protocol.
162
- #
163
- # A new Protocol instance set itself up as the IO's protocol (replacing any previous protocol).
164
- #
165
- # Normally you won't need to override this method. Override {#on_open} instead.
166
- def initialize io, options = nil
167
- @timeout ||= nil
168
- @send_locker = Mutex.new
169
- @ping_locker = Mutex.new
170
- @locker = Mutex.new
171
- @io = io
172
- @options = options
173
- touch
174
- @locker.synchronize do
175
- Iodine.switch_protocol @io.to_io, self
176
- on_open
177
- end
178
- end
179
-
180
- # Called by Iodine whenever there is data in the IO's read buffer.
181
- #
182
- # Normally you won't need to override this method. Override {#on_message} instead.
183
- def call
184
- return unless @locker.try_lock
185
- begin
186
- data = read
187
- if data
188
- on_message(data)
189
- # data.clear
190
- end
191
- ensure
192
- @locker.unlock
193
- end
194
- end
195
-
196
-
197
- # This method is used by Iodine invoke a timeout review.
198
- #
199
- # Normally you won't need to override this method. See {#ping}
200
- def timeout? time
201
- return unless @ping_locker.try_lock
202
- begin
203
- touch && ping if @timeout && !@send_locker.locked? && ( (time - @last_active) > @timeout )
204
- ensure
205
- @ping_locker.unlock
206
- end
207
- end
208
-
209
- protected
210
- # This method is used by Iodine to create the IO handler whenever a new connection is established.
211
- #
212
- # Normally you won't need to override this method.
213
- def self.accept io, ssl
214
- ssl ? SSLConnector.new(io, self) : self.new(io)
215
- rescue
216
- io.close unless io.closed?
217
- raise
218
- end
219
- # This methos updates the timeout "watch", signifying the IO was active.
220
- def touch
221
- @last_active = Iodine.time
222
- end
223
-
224
- # reads from the IO up to the specified number of bytes (defaults to ~1Mb).
225
- def read_ssl size
226
- @send_locker.synchronize do
227
- data = String.new
228
- begin
229
- (data << @io.read_nonblock(size, Thread.current[:buffer]).to_s) until data.bytesize >= size
230
- rescue OpenSSL::SSL::SSLErrorWaitReadable, IO::WaitReadable, IO::WaitWritable
231
-
232
- rescue IOError
233
- close
234
- rescue => e
235
- Iodine.warn "SSL Protocol read error: #{e.class.name} #{e.message} (closing connection)"
236
- close
237
- end
238
- return false if data.to_s.empty?
239
- touch
240
- data
241
- end
242
- end
243
-
244
-
245
- end
246
-
2
+ # The Protocol class is used only for documenting the Protocol API, it will not be included when requiring `iodine`.
3
+ #
4
+ # A dynamic (stateful) protocol is defined as a Ruby class instance which is in control of one single connection.
5
+ #
6
+ # It is called dynamic because it is dynamically allocated for each connection and then discarded,
7
+ # also it sounded better then calling it "the stateful protocol", even though that's what it actually is.
8
+ #
9
+ # It is (mostly) thread-safe as long as it's operations are limited to the scope
10
+ # of the object.
11
+ #
12
+ # <b>The Callbacks</b>
13
+ #
14
+ # A protocol class <b>MUST</b> contain ONE of the following callbacks:
15
+ #
16
+ # on_data:: called whened there's data available to be read, but no data was read just yet. `on_data` will not be called again untill all the existing network buffer was read (edge triggered event).
17
+ # on_message(buffer):: the default `on_data` implementation creates a 1Kb buffer and reads data while recycling the same String memory space. The buffer is forwarded to the `on_message` callback before being recycled. The buffer object will be over-written once `on_message` returns, so creating a persistent copy requires `buffer.dup`.
18
+ #
19
+ # A protocol class <b>MAY</b> contain any of the following optional callbacks:
20
+ #
21
+ # on_open:: called after a new connection was accepted and the protocol was linked with Iodine's Protocol API. Initialization should be performed here.
22
+ # ping:: called whenever timeout was reached. The default implementation will close the connection unless a protocol task ({Protocol#defer}, `on_data` or `on_message`) are busy in the background.
23
+ # on_shutdown:: called if the connection is still open while the server is shutting down. This allows the protocol to send a "going away" frame before the connection is closed and `on_close` is called.
24
+ # on_close:: called after a connection was closed, for any cleanup (if any).
25
+ #
26
+ # WARNING: for thread safety and connection management, `on_open`, `on_shutdown`, `on_close` and `ping` will all be performed within the reactor's main thread.
27
+ # Do not run long running tasks within these callbacks, or the server might block while you do.
28
+ # Use {#defer} to run protocol related tasks (this locks the connection, preventing it from running more then one task at a time and offering thread safety),
29
+ # or {#run} to run asynchronous tasks that aren't protocol related.
30
+ #
31
+ # <b>The API:</b>
32
+ #
33
+ # After a new connection is accepted and a new protocol object is created, the protocol will be linked with Iodine's Protocol API.
34
+ # Only the main protocol will be able to access the API within `initialize`, so it's best to use `on_open` for any Initialization required.
35
+ #
36
+ module Protocol
37
+ end
247
38
  end