stompserver 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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