stompserver 0.9.5 → 0.9.6

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.
@@ -1,3 +1,10 @@
1
+ == 0.9.6 / 31 Oct 2006
2
+
3
+ * Added simple code to handle denial of service attacks from large headers
4
+ * Fixed a multitude of bugs (and added tests) found by our small but growing
5
+ community of users
6
+ * more coming soom (thanks to snacktime)
7
+
1
8
  == 0.9.5 / 18 Oct 2006
2
9
 
3
10
  * Removed eventmachine dependency in the gem
@@ -41,20 +41,20 @@ class QueueManager
41
41
  end
42
42
 
43
43
  def ack(user, frame)
44
- pending_size = @pending[user]
44
+ pending_size = @pending[user].size
45
45
  msgid = frame.headers['message-id']
46
46
  @pending[user].delete_if { |pf| pf.headers['message-id'] == msgid }
47
- raise "Message (#{msgid}) not found" if pending_size == @pending[user]
47
+ raise "Message (#{msgid}) not found" if pending_size == @pending[user].size
48
48
  @journal.delete(msgid)
49
49
  end
50
50
 
51
51
  def disconnect(user)
52
- @pending[user].each do |frame|
53
- sendmsg(frame)
54
- end
55
52
  @queues.each do |dest, queue|
56
53
  queue.delete_if { |qu| qu.user == user }
57
54
  end
55
+ @pending[user].each do |frame|
56
+ sendmsg(frame)
57
+ end
58
58
  end
59
59
 
60
60
  def send_to_user(frame, user)
@@ -54,7 +54,7 @@ class StompFrameRecognizer
54
54
  end
55
55
 
56
56
  def parse_header
57
- if match = @buffer.match(/^\s*(\S+)$((?:\s*.*?\s*:\s*.*?\s*$)*)$\r?\n$\r?\n/)
57
+ if match = @buffer.match(/^\s*(\S+)$\r?\n((?:[ \t]*.*?[ \t]*:[ \t]*.*?[ \t]*$\r?\n)*)\r?\n/)
58
58
  @frame.command, headers = match.captures
59
59
  @buffer = match.post_match
60
60
  headers.split(/\n/).each do |data|
@@ -65,6 +65,10 @@ class StompFrameRecognizer
65
65
 
66
66
  # body_length is nil, if there is no content-length, otherwise it is the length (as in integer)
67
67
  @body_length = @frame.headers['content-length'] && @frame.headers['content-length'].to_i
68
+ else
69
+ # saftey valve to dump bad (DOSA?) headers/frames
70
+ # this should be a constant or read out of a configuration file
71
+ raise RuntimeError.new("Invalid stompframe (possible DOSA)") if @buffer.length > 4096
68
72
  end
69
73
  end
70
74
 
@@ -6,7 +6,7 @@ require 'queue_manager'
6
6
  require 'frame_journal'
7
7
 
8
8
  module StompServer
9
- VERSION = '0.9.5'
9
+ VERSION = '0.9.6'
10
10
  VALID_COMMANDS = [:connect, :send, :subscribe, :unsubscribe, :begin, :commit, :abort, :ack, :disconnect]
11
11
 
12
12
  def self.setup(j = FrameJournal.new, tm = TopicManager.new, qm = QueueManager.new(j))
@@ -43,6 +43,8 @@ module StompServer
43
43
  raise "Unhandled frame: #{cmd}" unless VALID_COMMANDS.include?(cmd)
44
44
  raise "Not connected" if !@connected && cmd != :connect
45
45
 
46
+ puts "process_frame #{cmd}" if $DEBUG
47
+
46
48
  # I really like this code, but my needs are a little trickier
47
49
  #
48
50
 
@@ -50,11 +52,18 @@ module StompServer
50
52
  handle_transaction(frame, trans, cmd)
51
53
  else
52
54
  cmd = :sendmsg if cmd == :send
55
+ puts "Execute #{cmd}" if $DEBUG
53
56
  send(cmd, frame)
54
57
  end
55
58
 
56
59
  send_receipt(frame.headers['receipt']) if frame.headers['receipt']
57
60
  end
61
+
62
+ # here is how we can stop, of course there is currently no
63
+ # way to get here
64
+ def shutdown(msg)
65
+ EventMachine::stop_event_loop
66
+ end
58
67
 
59
68
  def handle_transaction(frame, trans, cmd)
60
69
  if [:begin, :commit, :abort].include?(cmd)
@@ -84,7 +93,8 @@ module StompServer
84
93
 
85
94
  def subscribe(frame)
86
95
  if frame.dest =~ %r|^/queue|
87
- @@queue_manager.subscribe(frame.dest, self)
96
+ ack = frame.headers['ack'] == 'client'
97
+ @@queue_manager.subscribe(frame.dest, self, ack)
88
98
  else
89
99
  @@topic_manager.subscribe(frame.dest, self)
90
100
  end
@@ -92,9 +102,9 @@ module StompServer
92
102
 
93
103
  def unsubscribe(frame)
94
104
  if frame.dest =~ %r|^/queue|
95
- @@queue_manager.unsubscribe(self)
105
+ @@queue_manager.unsubscribe(frame.dest, self)
96
106
  else
97
- @@topic_manager.unsubscribe(self)
107
+ @@topic_manager.unsubscribe(frame.dest, self)
98
108
  end
99
109
  end
100
110
 
@@ -17,6 +17,7 @@ class TestQueues < Test::Unit::TestCase
17
17
  @headers = { 'destination' => dest, 'message-id' => id }
18
18
  @data = msg
19
19
  end
20
+ def body ; @data ; end
20
21
  def to_s ; @data ; end
21
22
  end
22
23
 
@@ -97,4 +98,19 @@ class TestQueues < Test::Unit::TestCase
97
98
  assert_equal('', u2.data)
98
99
  end
99
100
 
100
- end
101
+ def test_sendmsg_ack
102
+ u = UserMock.new
103
+ t = 'fooack'
104
+ @t.subscribe(t, u, true)
105
+
106
+ m1 = MessageMock.new(t, 'foomsg')
107
+ @t.sendmsg(m1)
108
+ assert_equal(m1.data, u.data)
109
+ assert_equal('MESSAGE', m1.command)
110
+ @t.disconnect(u)
111
+
112
+ u2 = UserMock.new
113
+ @t.subscribe(t, u2)
114
+ assert_equal(m1.data, u2.data)
115
+ end
116
+ end
@@ -57,8 +57,20 @@ FRAME
57
57
  assert_equal("bar2", f.headers["foo2"])
58
58
  assert_equal("message body 2\n", f.body)
59
59
  end
60
+
61
+ def test_double2
62
+ data = "SEND\ndestination:/queue/f\ncontent-length: 33\ncontent-type: text/plain; charset=UTF-8\n\n1: Tue Oct 24 12:19:52 -0400 2006\000SEND\ndestination:/queue/f\ncontent-length: 33\ncontent-type: text/plain; charset=UTF-8\n\n2: Tue Oct 24 12:19:52 -0400 2006\000"
63
+
64
+ @sfr << data
65
+ assert_equal(2, @sfr.frames.size)
66
+ f = @sfr.frames.shift
67
+ assert_equal(1, @sfr.frames.size)
68
+ assert_equal("SEND", f.command)
69
+ assert_equal("/queue/f", f.headers["destination"])
70
+ assert_match(/1: Tue/, f.body)
71
+ end
60
72
 
61
- def test_partialframe
73
+ def test_partialframe
62
74
  @sfr << <<FRAME
63
75
  COMMAND
64
76
  name:value
@@ -238,4 +238,25 @@ class TestStompServer < Test::Unit::TestCase
238
238
  ss2.stomp("SUBSCRIBE", {"destination" => '/queue/foo'})
239
239
  assert_match(/Hi Pat/, ss2.sent)
240
240
  end
241
+
242
+ def test_ack_queue_message
243
+ ss1 = MockStompServer.make_client
244
+ ss1.stomp("SEND", {'destination' => '/queue/taqm'},
245
+ 'Hi Pat')
246
+ ss1.stomp("DISCONNECT")
247
+
248
+ ss2 = MockStompServer.make_client
249
+ ss2.stomp("SUBSCRIBE", {"destination" => '/queue/taqm', 'ack' => 'client'})
250
+ assert_match(/Hi Pat/, ss2.sent)
251
+ ss2.unbind
252
+
253
+ ss3 = MockStompServer.make_client
254
+ ss3.stomp("SUBSCRIBE", {"destination" => '/queue/taqm'})
255
+ assert_match(/Hi Pat/, ss3.sent)
256
+
257
+ assert_nothing_raised do
258
+ ss3.stomp("SUBSCRIBE", {"destination" => '/queue/taqm',
259
+ "message-id" => "msg-cmastomp-1"})
260
+ end
261
+ end
241
262
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: stompserver
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.9.5
7
- date: 2006-10-18 00:00:00 -04:00
6
+ version: 0.9.6
7
+ date: 2006-10-31 00:00:00 -05:00
8
8
  summary: A very light messaging server
9
9
  require_paths:
10
10
  - lib