onstomp 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changes
2
2
 
3
+ ## 1.0.5
4
+ * fixed a race condition that would occur if a user (or the failover extension)
5
+ tried to re-connect an OnStomp::Client instance within an `on_connection_closed`
6
+ (or similar) event handler. The fix is more of a band-aid at the moment,
7
+ and a proper fix will follow. Thanks to [alcy](https://github.com/alcy) for
8
+ helping me track this down.
9
+
3
10
  ## 1.0.4
4
11
  * fixed major oversight when using ruby 1.8.7 / jruby with an SSL connection.
5
12
  it will use blocking read/write methods if nonblocking methods are unavailable
data/README.md CHANGED
@@ -46,10 +46,36 @@ in the hopes of increasing performance.
46
46
  The `stomp` gem is a good gem that works well, I just desired a different
47
47
  style API for working with message brokers.
48
48
 
49
+ ## Gotchas
50
+
51
+ Both Ruby 1.8.7 and JRuby (as of 1.6.1) do not provide non-blocking read
52
+ or write methods for OpenSSL connections. While a gem named
53
+ [openssl-nonblock](https://github.com/tarcieri/openssl-nonblock) exists for
54
+ Ruby < 1.9.2, I have not personally used it and given that it's a C extension,
55
+ it may not be compatible with JRuby's openssl gem. When an OnStomp connection
56
+ is created, the socket (SSL or TCP) is checked to see whether or not the methods
57
+ `write_nonblock` and `read_nonblock` have been defined. If not, OnStomp will
58
+ fall back on `write` for writing and `readpartial` for reading. While both of
59
+ these methods will block, the use of `IO::select` should help mitigate their
60
+ effects. I initially missed this detail, so if you're using an older version
61
+ of OnStomp (pre 1.0.4) with Ruby 1.8.7 or JRuby, you either want to upgrade
62
+ your gem or avoid `stomp+ssl://` URIs like the plague.
63
+
64
+ The final "gotcha" is more of an advanced warning. When JRuby's support
65
+ for the Ruby 1.9 API stabilizes (and `read_nonblock` and `write_nonblock` are
66
+ available for OpenSSL connections), I will be dropping support for Ruby 1.8.x
67
+ entirely. This is probably a ways off yet, but when the time comes, I'll
68
+ post obvious warnings and increment the gem's major version. OnStomp 1.x
69
+ will always be compatible with Ruby 1.8.7+, OnStomp 2.x will be Ruby 1.9.x
70
+ only.
71
+
49
72
  ## Further Reading
50
73
 
74
+ * The [OnStomp YARD Documentation](http://mdvlrb.com/onstomp/)
75
+ * The [OnStomp Github Wiki](https://github.com/meadvillerb/onstomp/wiki)
76
+ * Some [Contrived Examples](https://github.com/meadvillerb/onstomp/tree/master/examples)
51
77
  * A {file:extra_doc/UserNarrative.md User's Narrative}
52
- * A {file:extra_doc/DeveloperNarrative.md Developers's Narrative}
78
+ * The {file:extra_doc/API.md OnStomp API Promise} (sort of)
53
79
  * A {file:extra_doc/CHANGELOG.md History of Changes}
54
80
 
55
81
  ## License
data/examples/basic.rb CHANGED
@@ -6,21 +6,21 @@ puts "Starting demo"
6
6
  puts "----------------------------"
7
7
 
8
8
  running = true
9
- client = OnStomp::Client.new("stomp://localhost")
9
+ client = OnStomp::Client.new("stomp://localhost:61613")
10
10
  client.connect
11
11
 
12
12
  puts "Connected to broker using protocol #{client.connection.version}"
13
13
 
14
- client.subscribe("/queue/onstomp/test") do |message|
14
+ client.subscribe("/queue/onstomp_test") do |message|
15
15
  puts "Received: '#{message.body}'"
16
16
  if message.body == 'finished'
17
17
  running = false
18
18
  end
19
19
  end
20
20
 
21
- client.send("/queue/onstomp/test", "hello world")
22
- client.send("/queue/onstomp/test", "this is a simple demo of onstomp")
23
- client.send("/queue/onstomp/test", "finished")
21
+ client.send("/queue/onstomp_test", "hello world")
22
+ client.send("/queue/onstomp_test", "this is a simple demo of onstomp")
23
+ client.send("/queue/onstomp_test", "finished")
24
24
 
25
25
  Thread.pass while running
26
26
  client.disconnect
data/extra_doc/API.md CHANGED
@@ -33,18 +33,18 @@
33
33
  <tr>
34
34
  <td>{OnStomp::Interfaces::FrameMethods#ack ack}</td>
35
35
  <td>
36
- <code>ack(message_id, headers=&lt;optional hash&gt;)</code>
36
+ <code>ack(message_id, subscription_id, headers=&lt;optional hash&gt;)</code>
37
37
  </td>
38
38
  <td style="background-color: #bfb;">true</td>
39
- <td style="background-color: #fbb;">false</td>
39
+ <td style="background-color: #bfb;">true</td>
40
40
  </tr>
41
41
  <tr>
42
42
  <td>{OnStomp::Interfaces::FrameMethods#ack ack}</td>
43
43
  <td>
44
- <code>ack(message_id, subscription_id, headers=&lt;optional hash&gt;)</code>
44
+ <code>ack(message_id, headers=&lt;optional hash&gt;)</code>
45
45
  </td>
46
46
  <td style="background-color: #bfb;">true</td>
47
- <td style="background-color: #bfb;">true</td>
47
+ <td style="background-color: #fbb;">false</td>
48
48
  </tr>
49
49
  <tr>
50
50
  <td>{OnStomp::Interfaces::ClientEvents#after_receiving after_receiving}</td>
@@ -310,6 +310,14 @@
310
310
  <td style="background-color: #bfb;">true</td>
311
311
  <td style="background-color: #bfb;">true</td>
312
312
  </tr>
313
+ <tr>
314
+ <td>{OnStomp::Interfaces::ConnectionEvents#on_blocked on_blocked}</td>
315
+ <td>
316
+ <code>create_event_methods :blocked, :on</code>
317
+ </td>
318
+ <td style="background-color: #bfb;">true</td>
319
+ <td style="background-color: #bfb;">true</td>
320
+ </tr>
313
321
  <tr>
314
322
  <td>{OnStomp::Interfaces::ClientEvents#on_broker_beat on_broker_beat}</td>
315
323
  <td>
@@ -473,7 +481,7 @@
473
481
  <tr>
474
482
  <td>{OnStomp::Interfaces::FrameMethods#unsubscribe unsubscribe}</td>
475
483
  <td>
476
- <code>unsubscribe(id, headers=&lt;optional hash&gt;)</code>
484
+ <code>unsubscribe(subscribe_frame, headers=&lt;optional hash&gt;)</code>
477
485
  </td>
478
486
  <td style="background-color: #bfb;">true</td>
479
487
  <td style="background-color: #bfb;">true</td>
@@ -481,7 +489,7 @@
481
489
  <tr>
482
490
  <td>{OnStomp::Interfaces::FrameMethods#unsubscribe unsubscribe}</td>
483
491
  <td>
484
- <code>unsubscribe(subscribe_frame, headers=&lt;optional hash&gt;)</code>
492
+ <code>unsubscribe(id, headers=&lt;optional hash&gt;)</code>
485
493
  </td>
486
494
  <td style="background-color: #bfb;">true</td>
487
495
  <td style="background-color: #bfb;">true</td>
@@ -79,6 +79,9 @@ class OnStomp::Client
79
79
  # @param [{#to_sym => #to_s}] headers
80
80
  # @return [self]
81
81
  def connect headers={}
82
+ # FIXME: This is a quick fix to force the Threaded IO processor to
83
+ # complete its work before we establish a connection.
84
+ processor_inst.stop
82
85
  @connection = OnStomp::Connections.connect self, headers,
83
86
  { :'accept-version' => @versions.join(','), :host => @host,
84
87
  :'heart-beat' => @heartbeats.join(','), :login => @login,
@@ -30,7 +30,9 @@ class OnStomp::Components::ThreadedProcessor
30
30
  end
31
31
  rescue OnStomp::StopReceiver
32
32
  rescue Exception
33
- raise
33
+ # FIXME: This is pretty hacky, too. The problem is one of race
34
+ # conditions and how we access the connection.
35
+ raise if @run_thread == Thread.current
34
36
  end
35
37
  end
36
38
  self
@@ -63,13 +65,16 @@ class OnStomp::Components::ThreadedProcessor
63
65
  # and the {OnStomp::Client client} is still
64
66
  # {OnStomp::Client#connected? connected} after the thread is joined.
65
67
  def stop
66
- begin
67
- @run_thread.raise OnStomp::StopReceiver if @run_thread.alive?
68
- @run_thread.join
69
- rescue IOError, SystemCallError
70
- raise if @client.connected?
68
+ if @run_thread
69
+ begin
70
+ @run_thread.raise OnStomp::StopReceiver if @run_thread.alive?
71
+ @run_thread.join
72
+ rescue OnStomp::StopReceiver
73
+ rescue IOError, SystemCallError
74
+ raise if @client.connected?
75
+ end
76
+ @run_thread = nil
71
77
  end
72
- @run_thread = nil
73
78
  self
74
79
  end
75
80
  end
@@ -220,6 +220,13 @@ class OnStomp::Connections::Base
220
220
  rescue EOFError
221
221
  triggered_close $!.message
222
222
  rescue Exception
223
+ # TODO: Fix this potential race condition the right way.
224
+ # This is the problematic area! If the user (or failover library)
225
+ # try to reconnect the Client when the connection is closed, the
226
+ # exception won't be raised until the IO Processing thread has
227
+ # already been joined to the main thread. Thus, the connection gets
228
+ # re-established, the "dying" thread re-enters here, and immediately
229
+ # raises the exception that terminated it.
223
230
  triggered_close $!.message, :terminated
224
231
  raise
225
232
  end
@@ -53,14 +53,14 @@ module OnStomp::Connections::Heartbeating
53
53
  end
54
54
 
55
55
  # Returns true if client-side heartbeating is disabled, or
56
- # {#duration_since_transmitted} has not exceeded {#heartbeat_client_limit}
56
+ # {OnStomp::Connections::Base#duration_since_transmitted} has not exceeded {#heartbeat_client_limit}
57
57
  # @return [true, false]
58
58
  def client_pulse?
59
59
  heartbeat_client_limit == 0 || duration_since_transmitted <= heartbeat_client_limit
60
60
  end
61
61
 
62
62
  # Returns true if broker-side heartbeating is disabled, or
63
- # {#duration_since_received} has not exceeded {#heartbeat_broker_limit}
63
+ # {OnStomp::Connections::Base#duration_since_received} has not exceeded {#heartbeat_broker_limit}
64
64
  # @return [true, false]
65
65
  def broker_pulse?
66
66
  heartbeat_broker_limit == 0 || duration_since_received <= heartbeat_broker_limit
@@ -87,16 +87,17 @@ class OnStomp::Failover::Client
87
87
  # {OnStomp::Client#disconnect disconnect} on the {#active_client}
88
88
  def disconnect *args, &block
89
89
  if active_client
90
- Thread.pass until connected?
90
+ Thread.pass until connected? || @failed
91
91
  @client_mutex.synchronize do
92
92
  @disconnecting = true
93
- active_client.disconnect *args, &block
93
+ active_client.disconnect *args, &block if connected?
94
94
  end
95
95
  end
96
96
  end
97
97
 
98
98
  private
99
99
  def reconnect
100
+ @failed = false
100
101
  attempt = 1
101
102
  until connected? || retry_exceeded?(attempt)
102
103
  sleep_for_retry attempt
@@ -112,9 +113,9 @@ class OnStomp::Failover::Client
112
113
  trigger_failover_retry :after, attempt
113
114
  attempt += 1
114
115
  end
115
- connected?.tap do |b|
116
- b && trigger_failover_event(:connected, :on, active_client)
117
- end # <--- Until here
116
+ @failed = !connected?
117
+ trigger_failover_event(:connected, :on, active_client) unless @failed
118
+ !@failed
118
119
  end
119
120
 
120
121
  def retry_exceeded? attempt
@@ -7,7 +7,7 @@ module OnStomp
7
7
  # Minor / feature version
8
8
  MINOR = 0
9
9
  # Patch version
10
- PATCH = 4
10
+ PATCH = 5
11
11
  # Complete version
12
12
  VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
13
13
  end
@@ -117,6 +117,7 @@ module OnStomp
117
117
  { :'accept-version' => '1.1', :host => 'my host',
118
118
  :'heart-beat' => '30,110', :login => 'my login',
119
119
  :passcode => 's3cr3t' }, pending_events, 30, 50).and_return(connection)
120
+ processor.should_receive(:stop)
120
121
  processor.should_receive(:start)
121
122
  client.stub(:pending_connection_events => pending_events)
122
123
  client.versions = '1.1'
@@ -107,6 +107,27 @@ module OnStomp::Failover
107
107
  active_client.should_receive(:disconnect).with(:header1 => 'value 1')
108
108
  client.disconnect :header1 => 'value 1'
109
109
  end
110
+ it "should disconnect promptly if retrying exceeds maximum attempts" do
111
+ client.retry_attempts = 3
112
+ client.retry_delay = 0
113
+ active_client.stub(:connected? => false)
114
+ active_client.stub(:connect).and_return do
115
+ active_client.stub(:connected? => true)
116
+ end
117
+ client.connect
118
+ actual_disconnect = client.method(:disconnect)
119
+ client.stub(:disconnect).and_return do |*args|
120
+ active_client.stub(:connect => false)
121
+ active_client.stub(:connected? => false)
122
+ # Fire this off in a separate thread, as would be the real case
123
+ t = Thread.new do
124
+ connection.trigger_event :on_closed, active_client, connection
125
+ end
126
+ Thread.pass while t.alive?
127
+ actual_disconnect.call *args
128
+ end
129
+ client.disconnect :header1 => 'value 1'
130
+ end
110
131
  end
111
132
 
112
133
  describe ".transmit" do
metadata CHANGED
@@ -1,71 +1,68 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: onstomp
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.5
4
5
  prerelease:
5
- version: 1.0.4
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Ian D. Eccles
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-04-20 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2011-10-13 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: rspec
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2156950640 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
18
+ requirements:
21
19
  - - ~>
22
- - !ruby/object:Gem::Version
20
+ - !ruby/object:Gem::Version
23
21
  version: 2.4.0
24
22
  type: :development
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: simplecov
28
23
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2156950640
25
+ - !ruby/object:Gem::Dependency
26
+ name: simplecov
27
+ requirement: &2156950180 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
34
32
  version: 0.3.0
35
33
  type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: yard
39
34
  prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *2156950180
36
+ - !ruby/object:Gem::Dependency
37
+ name: yard
38
+ requirement: &2156949720 !ruby/object:Gem::Requirement
41
39
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
45
43
  version: 0.6.0
46
44
  type: :development
47
- version_requirements: *id003
48
- - !ruby/object:Gem::Dependency
49
- name: rake
50
45
  prerelease: false
51
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *2156949720
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &2156949340 !ruby/object:Gem::Requirement
52
50
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
57
55
  type: :development
58
- version_requirements: *id004
59
- description: Client library for message passing with brokers that support the Stomp protocol.
60
- email:
56
+ prerelease: false
57
+ version_requirements: *2156949340
58
+ description: Client library for message passing with brokers that support the Stomp
59
+ protocol.
60
+ email:
61
61
  - ian.eccles@gmail.com
62
62
  executables: []
63
-
64
63
  extensions: []
65
-
66
64
  extra_rdoc_files: []
67
-
68
- files:
65
+ files:
69
66
  - .autotest
70
67
  - .gitignore
71
68
  - .rspec
@@ -80,7 +77,6 @@ files:
80
77
  - examples/openuri.rb
81
78
  - extra_doc/API.md
82
79
  - extra_doc/API.md.erb
83
- - extra_doc/DeveloperNarrative.md
84
80
  - extra_doc/UserNarrative.md
85
81
  - lib/onstomp.rb
86
82
  - lib/onstomp/client.rb
@@ -197,32 +193,29 @@ files:
197
193
  - yard_extensions.rb
198
194
  homepage: http://github.com/meadvillerb/onstomp
199
195
  licenses: []
200
-
201
196
  post_install_message:
202
197
  rdoc_options: []
203
-
204
- require_paths:
198
+ require_paths:
205
199
  - lib
206
- required_ruby_version: !ruby/object:Gem::Requirement
200
+ required_ruby_version: !ruby/object:Gem::Requirement
207
201
  none: false
208
- requirements:
209
- - - ">="
210
- - !ruby/object:Gem::Version
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
211
205
  version: 1.8.7
212
- required_rubygems_version: !ruby/object:Gem::Requirement
206
+ required_rubygems_version: !ruby/object:Gem::Requirement
213
207
  none: false
214
- requirements:
215
- - - ">="
216
- - !ruby/object:Gem::Version
217
- version: "0"
208
+ requirements:
209
+ - - ! '>='
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
218
212
  requirements: []
219
-
220
213
  rubyforge_project: onstomp-core
221
- rubygems_version: 1.7.2
214
+ rubygems_version: 1.8.10
222
215
  signing_key:
223
216
  specification_version: 3
224
217
  summary: Client for message queues implementing the Stomp protocol interface.
225
- test_files:
218
+ test_files:
226
219
  - spec/onstomp/client_spec.rb
227
220
  - spec/onstomp/components/frame_headers_spec.rb
228
221
  - spec/onstomp/components/frame_spec.rb
@@ -286,4 +279,3 @@ test_files:
286
279
  - spec/support/custom_argument_matchers.rb
287
280
  - spec/support/frame_matchers.rb
288
281
  - spec/support/shared_frame_method_examples.rb
289
- has_rdoc:
@@ -1,123 +0,0 @@
1
- # A Narrative for Developers
2
-
3
- This document explores the `OnStomp` library through a narrative aimed at end
4
- developers who wish to extend or modify the library. It will start with the
5
- basics and work through the important code through exposition and examples.
6
- It may be helpful to
7
- review the [STOMP specification](http://stomp.github.com/index.html) before
8
- diving into this document. It's also important to note that `onstomp` can
9
- only be used with Ruby 1.8.7+. Support for Rubies prior to 1.8.7 does not
10
- exist, and even requiring the library in your code will probably generate
11
- errors.
12
-
13
- ## Clients & Frames
14
-
15
- An instance of {OnStomp::Client} is the primary way a user will interact
16
- with the `OnStomp` gem. It provides the helper methods to generate STOMP
17
- frames by including the {OnStomp::Interfaces::FrameMethods} mixin, and allows
18
- binding event callbacks by including {OnStomp::Interfaces::ClientEvents}
19
- (which in turn includes {OnStomp::Interfaces::EventManager}.) Every client
20
- instance will provide the same frame methods, regardless of the version of
21
- the STOMP protocol being used. This is accomplished through by a client's use
22
- of {OnStomp::Connections connections} and
23
- {OnStomp::Connections::Serializers serializers} to actually generate the
24
- frames and convert them to their appropriate string representation. You can
25
- read more about these components in a later section.
26
-
27
- All of the frame methods (eg: {OnStomp::Interfaces::FrameMethods#send send},
28
- {OnStomp::Interfaces::FrameMethods#send unsubscribe}) will either generate a
29
- new {OnStomp::Components::Frame} instance or raise an error if the STOMP
30
- protocol version negotiated between broker and client does not support
31
- the requested command (eg: STOMP 1.0 does not support NACK frames.) All
32
- frame instances are composed of a {OnStomp::Components::Frame#command command},
33
- a set of {OnStomp::Components::Frame#headers headers}, and a
34
- {OnStomp::Components::Frame#body body}.
35
-
36
- A frame's {OnStomp::Components::Frame#command command} attribute is a string
37
- that matches the corresponding STOMP command (eg: SEND, RECEIPT) with one
38
- exception: heart-beats. The STOMP 1.1 protocol supports "heart-beating" to
39
- let brokers and clients know that the connection is still active on the other
40
- end by sending bare line-feed (ie: `\n`) characters between frame exchanges.
41
- As a result, calling {OnStomp::Interfaces::FrameMethods#beat beat} on a client
42
- will generate a frame whose command attribute is `nil`. This in turn lets the
43
- serializer know that this isn't a true frame and it will convert it to
44
- +"\n"+ instead of performing the normal serialization operation.
45
-
46
- A frame's {OnStomp::Components::FrameHeaders headers} behave much like a
47
- standard Ruby `Hash` but with a few important differences. First, the order
48
- that header names were added is preserved. This is also true of Ruby 1.9+
49
- hashes but not those of Ruby 1.8.7, and as a result there is some code
50
- to maintain the ordering when running 1.8.7. Second, accessing headers
51
- is somewhat indifferent:
52
-
53
- frame[:some_header] = 'a value'
54
- frame["some_header"] #=> 'a value'
55
-
56
- What actually happens is all header names are converted to `Symbol`s
57
- (by calling `obj.to_sym`) before any setting or getting takes place. Using
58
- an object as a header name that does not respond to `to_sym` will raise an
59
- error. The final major difference between headers and hashes is that header
60
- values are only strings:
61
-
62
- frame[:another_header] = 42
63
- frame[:another_header] #=> '42'
64
-
65
- If the object passed as a header value cannot be converted to a string an
66
- error will be raised.
67
-
68
- For most kinds of frames, the {OnStomp::Components::Frame#body body} attribute
69
- will often be an empty string or `nil`. The only frames that support
70
- non-empty bodies are SEND, MESSAGE, and ERROR.
71
-
72
- ## Events
73
-
74
- ### Frame-centric Events
75
-
76
- Event triggering sequence for client generated frames:
77
-
78
- 1. `client.send ...` is invoked and a SEND frame is created
79
- 1. The event `before_transmitting` is triggered for the SEND frame
80
- 1. The event `before_send` is triggered for the SEND frame
81
- 1. The SEND frame is added to the {OnStomp::Connections::Base connection}'s
82
- write buffer
83
- 1. Some amount of time passes
84
- 1. The SEND frame is serialized and fully written to the broker.
85
- 1. The event `after_transmitting` is triggered for the SEND frame
86
- 1. The event `on_send` is triggered for the SEND frame
87
-
88
- Event triggering sequence for broker generated frames:
89
-
90
- 1. The broker writes a MESSAGE frame to the TCP/IP socket
91
- 1. Some amount of time passes
92
- 1. The client fully reads and de-serializes the MESSAGE frame
93
- 1. The event `before_receiving` is triggered for the MESSAGE frame
94
- 1. The event `before_message` is triggered for the MESSAGE frame
95
- 1. The event `after_receiving` is triggered for the MESSAGE frame
96
- 1. The event `on_message` is triggered for the MESSAGE frame
97
-
98
- ### Connection-centric Events
99
-
100
- Event trigger sequence for connection events:
101
-
102
- 1. An IO error occurs while the connection is reading or writing
103
- 1. The connection closes its socket
104
- 1. The connection triggers :on\_terminated
105
- 1. The connection triggers :on\_closed
106
-
107
- ## Subscription and Receipt Management
108
-
109
- ### Subscription Manager
110
-
111
- ### Receipt Manager
112
-
113
- ## URI Based Configuration
114
-
115
- ## Processors
116
-
117
- ## Connections & Serializers
118
-
119
- ### Connections
120
-
121
- ### Serializers
122
-
123
-