stomp 1.2.4 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/CHANGELOG.rdoc +11 -0
  2. data/README.rdoc +38 -26
  3. data/Rakefile +3 -0
  4. data/bin/catstomp +34 -34
  5. data/bin/stompcat +36 -36
  6. data/examples/client11_ex1.rb +64 -55
  7. data/examples/client11_putget1.rb +47 -35
  8. data/examples/conn11_ex1.rb +59 -51
  9. data/examples/conn11_ex2.rb +59 -50
  10. data/examples/conn11_hb1.rb +35 -26
  11. data/examples/consumer.rb +25 -12
  12. data/examples/get11conn_ex1.rb +97 -89
  13. data/examples/get11conn_ex2.rb +55 -47
  14. data/examples/logexamp.rb +66 -52
  15. data/examples/logexamp_ssl.rb +66 -52
  16. data/examples/publisher.rb +21 -10
  17. data/examples/put11conn_ex1.rb +35 -24
  18. data/examples/putget11_rh1.rb +66 -56
  19. data/examples/slogger.rb +65 -52
  20. data/examples/ssl_uc1.rb +24 -13
  21. data/examples/ssl_uc1_ciphers.rb +28 -15
  22. data/examples/ssl_uc2.rb +26 -16
  23. data/examples/ssl_uc2_ciphers.rb +31 -18
  24. data/examples/ssl_uc3.rb +25 -14
  25. data/examples/ssl_uc3_ciphers.rb +31 -18
  26. data/examples/ssl_uc4.rb +26 -15
  27. data/examples/ssl_uc4_ciphers.rb +32 -19
  28. data/examples/ssl_ucx_default_ciphers.rb +25 -12
  29. data/examples/stomp11_common.rb +16 -15
  30. data/examples/topic_consumer.rb +23 -10
  31. data/examples/topic_publisher.rb +22 -8
  32. data/lib/client/utils.rb +116 -0
  33. data/lib/connection/heartbeats.rb +173 -0
  34. data/lib/connection/netio.rb +322 -0
  35. data/lib/connection/utf8.rb +294 -0
  36. data/lib/connection/utils.rb +104 -0
  37. data/lib/stomp/client.rb +127 -179
  38. data/lib/stomp/codec.rb +5 -2
  39. data/lib/stomp/connection.rb +109 -865
  40. data/lib/stomp/constants.rb +52 -33
  41. data/lib/stomp/errors.rb +56 -5
  42. data/lib/stomp/ext/hash.rb +4 -0
  43. data/lib/stomp/message.rb +49 -29
  44. data/lib/stomp/sslparams.rb +83 -71
  45. data/lib/stomp/version.rb +3 -1
  46. data/lib/stomp.rb +18 -9
  47. data/stomp.gemspec +58 -3
  48. data/test/test_client.rb +28 -1
  49. data/test/test_codec.rb +8 -2
  50. data/test/test_connection.rb +29 -0
  51. data/test/test_connection1p.rb +31 -16
  52. data/test/test_helper.rb +20 -3
  53. data/test/test_message.rb +8 -3
  54. data/test/test_ssl.rb +10 -4
  55. data/test/tlogger.rb +16 -15
  56. metadata +59 -4
data/examples/logexamp.rb CHANGED
@@ -4,64 +4,78 @@ require 'rubygems'
4
4
  require 'stomp'
5
5
  require 'logger' # for the 'local' logger
6
6
  #
7
- $:.unshift(File.dirname(__FILE__))
7
+ if Kernel.respond_to?(:require_relative)
8
+ require_relative("./slogger")
9
+ else
10
+ $LOAD_PATH << File.dirname(__FILE__)
11
+ require "slogger"
12
+ end
8
13
  #
9
- require 'slogger'
14
+ # == A STOMP::Connection program which uses the callback logging facility.
10
15
  #
11
- # A STOMP::Connection program which uses the callback logging facility.
12
- #
13
- llog = Logger::new(STDOUT)
14
- llog.level = Logger::DEBUG
15
- llog.debug "LE Starting"
16
+ class LoggerExample
17
+ # Initialize.
18
+ def initialize
19
+ end
20
+ # Run example.
21
+ def run
22
+ llog = Logger::new(STDOUT)
23
+ llog.level = Logger::DEBUG
24
+ llog.debug "LE Starting"
16
25
 
17
- # //////////////////////////////////////////////////////////////////////////////
18
- mylog = Slogger::new # The client provided STOMP callback logger
26
+ # //////////////////////////////////////////////////////////////////////////////
27
+ mylog = Slogger::new # The client provided STOMP callback logger
19
28
 
20
- # //////////////////////////////////////////////////////////////////////////////
21
- user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest'
22
- password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest'
23
- host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost'
24
- port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61613
25
- # //////////////////////////////////////////////////////////////////////////////
26
- # A hash type connect *MUST* be used to enable callback logging.
27
- # //////////////////////////////////////////////////////////////////////////////
28
- hash = { :hosts => [
29
- {:login => user, :passcode => password, :host => 'noonehome', :port => 2525},
30
- {:login => user, :passcode => password, :host => host, :port => port},
31
- ],
32
- :logger => mylog, # This enables callback logging!
33
- :max_reconnect_attempts => 5,
34
- }
29
+ # //////////////////////////////////////////////////////////////////////////////
30
+ user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest'
31
+ password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest'
32
+ host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost'
33
+ port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61613
34
+ # //////////////////////////////////////////////////////////////////////////////
35
+ # A hash type connect *MUST* be used to enable callback logging.
36
+ # //////////////////////////////////////////////////////////////////////////////
37
+ hash = { :hosts => [
38
+ {:login => user, :passcode => password, :host => 'noonehome', :port => 2525},
39
+ {:login => user, :passcode => password, :host => host, :port => port},
40
+ ],
41
+ :logger => mylog, # This enables callback logging!
42
+ :max_reconnect_attempts => 5,
43
+ }
35
44
 
36
- # //////////////////////////////////////////////////////////////////////////////
37
- # For a Connection:
38
- llog.debug "LE Connection processing starts"
39
- conn = Stomp::Connection.new(hash)
40
- conn.disconnect
41
- # //////////////////////////////////////////////////////////////////////////////
42
- llog.debug "LE Connection processing complete"
45
+ # //////////////////////////////////////////////////////////////////////////////
46
+ # For a Connection:
47
+ llog.debug "LE Connection processing starts"
48
+ conn = Stomp::Connection.new(hash)
49
+ conn.disconnect
50
+ # //////////////////////////////////////////////////////////////////////////////
51
+ llog.debug "LE Connection processing complete"
43
52
 
44
- # //////////////////////////////////////////////////////////////////////////////
45
- # For a Client:
46
- llog.debug "LE Client processing starts"
47
- conn = Stomp::Client.new(hash)
48
- conn.close
49
- # //////////////////////////////////////////////////////////////////////////////
50
- llog.debug "LE Client processing complete"
53
+ # //////////////////////////////////////////////////////////////////////////////
54
+ # For a Client:
55
+ llog.debug "LE Client processing starts"
56
+ conn = Stomp::Client.new(hash)
57
+ conn.close
58
+ # //////////////////////////////////////////////////////////////////////////////
59
+ llog.debug "LE Client processing complete"
51
60
 
52
- # //////////////////////////////////////////////////////////////////////////////
53
- # For a Connection with other calls:
54
- llog.debug "LE Connection Enhanced processing starts"
55
- conn = Stomp::Connection.new(hash)
56
- #
57
- dest = "/queue/loggerq1"
58
- conn.publish dest, "a logger message"
59
- conn.subscribe dest
60
- msg = conn.receive
61
- conn.disconnect
62
- # //////////////////////////////////////////////////////////////////////////////
63
- llog.debug "LE Connection Enhanced processing complete"
61
+ # //////////////////////////////////////////////////////////////////////////////
62
+ # For a Connection with other calls:
63
+ llog.debug "LE Connection Enhanced processing starts"
64
+ conn = Stomp::Connection.new(hash)
65
+ #
66
+ dest = "/queue/loggerq1"
67
+ conn.publish dest, "a logger message"
68
+ conn.subscribe dest
69
+ msg = conn.receive
70
+ conn.disconnect
71
+ # //////////////////////////////////////////////////////////////////////////////
72
+ llog.debug "LE Connection Enhanced processing complete"
73
+
74
+ # //////////////////////////////////////////////////////////////////////////////
75
+ llog.debug "LE Ending"
76
+ end
77
+ end
78
+ e = LoggerExample.new
79
+ e.run
64
80
 
65
- # //////////////////////////////////////////////////////////////////////////////
66
- llog.debug "LE Ending"
67
81
 
@@ -4,64 +4,78 @@ require 'rubygems'
4
4
  require 'stomp'
5
5
  require 'logger' # for the 'local' logger
6
6
  #
7
- $:.unshift(File.dirname(__FILE__))
7
+ if Kernel.respond_to?(:require_relative)
8
+ require_relative("./slogger")
9
+ else
10
+ $LOAD_PATH << File.dirname(__FILE__)
11
+ require "slogger"
12
+ end
8
13
  #
9
- require 'slogger'
14
+ # == A STOMP::Connection program which uses the callback logging facility.
10
15
  #
11
- # A STOMP::Connection program which uses the callback logging facility.
12
- #
13
- llog = Logger::new(STDOUT)
14
- llog.level = Logger::DEBUG
15
- llog.debug "LESSL Starting"
16
+ class SSLLoggerExample
17
+ # Initialize.
18
+ def initialize
19
+ end
20
+ # Run example.
21
+ def run
22
+ llog = Logger::new(STDOUT)
23
+ llog.level = Logger::DEBUG
24
+ llog.debug "LESSL Starting"
16
25
 
17
- # //////////////////////////////////////////////////////////////////////////////
18
- mylog = Slogger::new # The client provided STOMP callback logger
26
+ # //////////////////////////////////////////////////////////////////////////////
27
+ mylog = Slogger::new # The client provided STOMP callback logger
19
28
 
20
- # //////////////////////////////////////////////////////////////////////////////
21
- user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest'
22
- password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest'
23
- host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost'
24
- port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612
25
- # //////////////////////////////////////////////////////////////////////////////
26
- # A hash type connect *MUST* be used to enable callback logging.
27
- # //////////////////////////////////////////////////////////////////////////////
28
- hash = { :hosts => [
29
- {:login => user, :passcode => password, :host => host, :port => port,
30
- :ssl => true}, # Or provide your insance of SSLParams instead
31
- ],
32
- :logger => mylog, # This enables callback logging!
33
- :max_reconnect_attempts => 2,
34
- }
29
+ # //////////////////////////////////////////////////////////////////////////////
30
+ user = ENV['STOMP_USER'] ? ENV['STOMP_USER'] : 'guest'
31
+ password = ENV['STOMP_PASSWORD'] ? ENV['STOMP_PASSWORD'] : 'guest'
32
+ host = ENV['STOMP_HOST'] ? ENV['STOMP_HOST'] : 'localhost'
33
+ port = ENV['STOMP_PORT'] ? ENV['STOMP_PORT'].to_i : 61612
34
+ # //////////////////////////////////////////////////////////////////////////////
35
+ # A hash type connect *MUST* be used to enable callback logging.
36
+ # //////////////////////////////////////////////////////////////////////////////
37
+ hash = { :hosts => [
38
+ {:login => user, :passcode => password, :host => host, :port => port,
39
+ :ssl => true}, # Or provide your insance of SSLParams instead
40
+ ],
41
+ :logger => mylog, # This enables callback logging!
42
+ :max_reconnect_attempts => 2,
43
+ }
35
44
 
36
- # //////////////////////////////////////////////////////////////////////////////
37
- # For a Connection:
38
- llog.debug "LESSL Connection processing starts"
39
- conn = Stomp::Connection.new(hash)
40
- conn.disconnect
41
- # //////////////////////////////////////////////////////////////////////////////
42
- llog.debug "LESSL Connection processing complete"
45
+ # //////////////////////////////////////////////////////////////////////////////
46
+ # For a Connection:
47
+ llog.debug "LESSL Connection processing starts"
48
+ conn = Stomp::Connection.new(hash)
49
+ conn.disconnect
50
+ # //////////////////////////////////////////////////////////////////////////////
51
+ llog.debug "LESSL Connection processing complete"
43
52
 
44
- # //////////////////////////////////////////////////////////////////////////////
45
- # For a Client:
46
- llog.debug "LESSL Client processing starts"
47
- conn = Stomp::Client.new(hash)
48
- conn.close
49
- # //////////////////////////////////////////////////////////////////////////////
50
- llog.debug "LESSL Client processing complete"
53
+ # //////////////////////////////////////////////////////////////////////////////
54
+ # For a Client:
55
+ llog.debug "LESSL Client processing starts"
56
+ conn = Stomp::Client.new(hash)
57
+ conn.close
58
+ # //////////////////////////////////////////////////////////////////////////////
59
+ llog.debug "LESSL Client processing complete"
51
60
 
52
- # //////////////////////////////////////////////////////////////////////////////
53
- # For a Connection with other calls:
54
- llog.debug "LESSL Connection Enhanced processing starts"
55
- conn = Stomp::Connection.new(hash)
56
- #
57
- dest = "/queue/loggerq1"
58
- conn.publish dest, "a logger message"
59
- conn.subscribe dest
60
- msg = conn.receive
61
- conn.disconnect
62
- # //////////////////////////////////////////////////////////////////////////////
63
- llog.debug "LESSL Connection Enhanced processing complete"
61
+ # //////////////////////////////////////////////////////////////////////////////
62
+ # For a Connection with other calls:
63
+ llog.debug "LESSL Connection Enhanced processing starts"
64
+ conn = Stomp::Connection.new(hash)
65
+ #
66
+ dest = "/queue/loggerq1"
67
+ conn.publish dest, "a logger message"
68
+ conn.subscribe dest
69
+ msg = conn.receive
70
+ conn.disconnect
71
+ # //////////////////////////////////////////////////////////////////////////////
72
+ llog.debug "LESSL Connection Enhanced processing complete"
64
73
 
65
- # //////////////////////////////////////////////////////////////////////////////
66
- llog.debug "LESSL Ending"
74
+ # //////////////////////////////////////////////////////////////////////////////
75
+ llog.debug "LESSL Ending"
76
+ end
77
+ end
78
+ #
79
+ e = SSLLoggerExample.new
80
+ e.run
67
81
 
@@ -2,18 +2,29 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'stomp'
5
+ #
6
+ # == Example message publisher
7
+ #
8
+ class ExamplePublisher
9
+ # Initialize.
10
+ def initialize
11
+ end
12
+ # Run example.
13
+ def run
14
+ client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false")
15
+ message = "ronaldo #{ARGV[0]}"
5
16
 
6
- #client = Stomp::Client.new("", "", "localhost", 61613)
7
-
8
- client = Stomp::Client.new("failover://(stomp://:@localhost:61613,stomp://:@remotehost:61613)?initialReconnectDelay=5000&randomize=false&useExponentialBackOff=false")
9
- message = "ronaldo #{ARGV[0]}"
10
-
11
- for i in (1..300)
12
- puts "Sending message"
13
- client.send("/queue/ronaldo", "#{i}: #{message}", {:persistent => true})
14
- puts "(#{Time.now})Message sent: #{i}"
15
- sleep 1
17
+ for i in (1..50)
18
+ puts "Sending message"
19
+ client.publish("/queue/ronaldo", "#{i}: #{message}", {:persistent => true})
20
+ puts "(#{Time.now})Message sent: #{i}"
21
+ sleep 0.2
22
+ end
23
+ end
16
24
  end
25
+ #
26
+ e = ExamplePublisher.new
27
+ e.run
17
28
 
18
29
 
19
30
 
@@ -11,35 +11,46 @@ else
11
11
  require "stomp11_common"
12
12
  end
13
13
  include Stomp11Common
14
+
14
15
  #
15
- # Stomp 1.1 Publish Example
16
- # =========================
16
+ # == Stomp 1.1 Publish Example
17
17
  #
18
18
  # Purpose: to demonstrate sending messages using Stomp 1.1.
19
19
  #
20
- conn = get_connection() # Use helper method to obtain a Stomp#connection
21
- raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
22
- #
23
- # Publishing simple data is as it was with Stomp 1.0.
24
- #
25
- # Note: Stomp 1.1 brokers seem to prefer using '.' as delimiters in queue
26
- # name spaces. Hence, the queue name used here.
27
- #
28
- qname = "/queue/nodea.nodeb.nodec"
29
- data = "message payload"
30
- headers = {}
31
- #
32
- # The 'data' and 'headers' may be omitted, as with Stomp 1.0
33
- #
34
- 1.upto(nmsgs()) do |i|
35
- msg = "#{data}: #{i}"
36
- conn.publish qname, msg , headers
37
- puts "Sent data: #{msg}"
20
+ class Publish11Example1
21
+ # Initialize.
22
+ def initialize
23
+ end
24
+ # Run example.
25
+ def run
26
+ conn = get_connection() # Use helper method to obtain a Stomp#connection
27
+ raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
28
+ #
29
+ # Publishing simple data is as it was with Stomp 1.0.
30
+ #
31
+ # Note: Stomp 1.1 brokers seem to prefer using '.' as delimiters in queue
32
+ # name spaces. Hence, the queue name used here.
33
+ #
34
+ qname = "/queue/nodea.nodeb.nodec"
35
+ data = "message payload"
36
+ headers = {}
37
+ #
38
+ # The 'data' and 'headers' may be omitted, as with Stomp 1.0
39
+ #
40
+ puts "Writing #{nmsgs()} messages."
41
+ 1.upto(nmsgs()) do |i|
42
+ msg = "#{data}: #{i}"
43
+ conn.publish qname, msg , headers
44
+ puts "Sent data: #{msg}"
45
+ end
46
+ #
47
+ # And finally, disconnect.
48
+ #
49
+ conn.disconnect
50
+ end
38
51
  end
39
52
  #
40
- # And finally, disconnect.
41
- #
42
- conn.disconnect
43
-
53
+ e = Publish11Example1.new
54
+ e.run
44
55
 
45
56
 
@@ -12,72 +12,82 @@ else
12
12
  end
13
13
  include Stomp11Common
14
14
  #
15
- # Stomp 1.1 Send/Receive Example - Repeated Headers
16
- # =================================================
15
+ # == Stomp 1.1 Send/Receive Example - Repeated Headers
17
16
  #
18
17
  # Purpose: to demonstrate sending and receiving using Stomp 1.1, and an unusual
19
18
  # aspect of the specification. What is demonstrated here is the use of
20
19
  # 'repeated headers'. Note that brokers MAY support repeated headers as
21
- # demonstrated, but are not required to provide this support. This example
22
- # should run against the Apollo broker. It will *not* currently run against
20
+ # demonstrated, but are not required to provide this support. This example
21
+ # should run against the Apollo broker. It will *not* currently run against
23
22
  # RabbitMQ. YMMV depending on your broker.
24
23
  #
25
24
  # See: http://stomp.github.com/stomp-specification-1.1.html#Repeated_Header_Entries
26
25
  #
27
- conn = get_connection() # Use helper method to obtain a Stomp#connection
28
- raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
29
- #
30
- # The gem supports repeated headers by allowing the 'value' part of a header
31
- # to be an Array instance.
32
- #
33
- # On 'publish', all values in the Array are placed on the wire and sent to the
34
- # broker in order.
35
- #
36
- # On 'receive', if repeated headers are detected, an Array instance is created
37
- # to hold the repeated values. This is presented the the calling client to
38
- # be processed per client requirements.
39
- #
40
- qname = "/queue/nodea.nodeb.nodec"
41
- data = "message payload: #{Time.now.to_f}"
42
- key2_repeats = ["key2val3", "key2val2", "key2val1" ]
43
- headers = {"key1" => "value1", # A normal header
44
- "key2" => key2_repeats, # A repeated header
45
- "key3" => "value3", # Another normal header
46
- }
47
- #
48
- # Ship it.
49
- #
50
- conn.publish qname, data , headers
51
- puts "Sent data: #{data}"
52
- #
53
- # Receive phase.
54
- #
55
- uuid = conn.uuid()
56
- conn.subscribe qname, {"id" => uuid}
57
- received = conn.receive
58
- conn.unsubscribe qname, {"id" => uuid}
59
- #
60
- # Check that we received what we sent.
61
- #
62
- raise "Unexpected payload" unless data == received.body
63
- raise "Missing key" unless received.headers["key2"]
64
- raise "Repeats not present" unless received.headers.has_value?(key2_repeats)
65
- raise "Unexpected repeat values" unless key2_repeats == received.headers["key2"]
66
- #
67
- # Demonstrate how to process repeated headers received by display of those
68
- # received headers for a visual check.
69
- #
70
- received.headers.each_pair do |k,v|
71
- if v.is_a?(Array)
72
- v.each do |e|
73
- puts "#{k}:#{e}"
26
+ class RepeatedHeadersExample
27
+ # Initialize.
28
+ def initialize
29
+ end
30
+ # Run example.
31
+ def run
32
+ conn = get_connection() # Use helper method to obtain a Stomp#connection
33
+ raise "Unexpected protocol level" if conn.protocol != Stomp::SPL_11
34
+ #
35
+ # The gem supports repeated headers by allowing the 'value' part of a header
36
+ # to be an Array instance.
37
+ #
38
+ # On 'publish', all values in the Array are placed on the wire and sent to the
39
+ # broker in order.
40
+ #
41
+ # On 'receive', if repeated headers are detected, an Array instance is created
42
+ # to hold the repeated values. This is presented the the calling client to
43
+ # be processed per client requirements.
44
+ #
45
+ qname = "/queue/nodea.nodeb.nodec"
46
+ data = "message payload: #{Time.now.to_f}"
47
+ key2_repeats = ["key2val3", "key2val2", "key2val1" ]
48
+ headers = {"key1" => "value1", # A normal header
49
+ "key2" => key2_repeats, # A repeated header
50
+ "key3" => "value3", # Another normal header
51
+ }
52
+ #
53
+ # Ship it.
54
+ #
55
+ conn.publish qname, data , headers
56
+ puts "Sent data: #{data}"
57
+ #
58
+ # Receive phase.
59
+ #
60
+ uuid = conn.uuid()
61
+ conn.subscribe qname, {"id" => uuid}
62
+ received = conn.receive
63
+ conn.unsubscribe qname, {"id" => uuid}
64
+ #
65
+ # Check that we received what we sent.
66
+ #
67
+ raise "Unexpected payload" unless data == received.body
68
+ raise "Missing key" unless received.headers["key2"]
69
+ raise "Repeats not present" unless received.headers.has_value?(key2_repeats)
70
+ raise "Unexpected repeat values" unless key2_repeats == received.headers["key2"]
71
+ #
72
+ # Demonstrate how to process repeated headers received by display of those
73
+ # received headers for a visual check.
74
+ #
75
+ received.headers.each_pair do |k,v|
76
+ if v.is_a?(Array)
77
+ v.each do |e|
78
+ puts "#{k}:#{e}"
79
+ end
80
+ else
81
+ puts "#{k}:#{v}"
82
+ end
74
83
  end
75
- else
76
- puts "#{k}:#{v}"
84
+ #
85
+ # And finally, disconnect.
86
+ #
87
+ conn.disconnect
77
88
  end
78
89
  end
79
90
  #
80
- # And finally, disconnect.
81
- #
82
- conn.disconnect
91
+ e = RepeatedHeadersExample.new
92
+ e.run
83
93