eventmachine 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,145 @@
1
+ # $Id: linetext2.rb 486 2007-07-29 17:15:12Z blackhedd $
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+
27
+
28
+ # In the grand, time-honored tradition of re-inventing the wheel, we offer
29
+ # here YET ANOTHER protocol that handles line-oriented data with interspersed
30
+ # binary text. This one trades away some of the performance optimizations of
31
+ # EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
32
+ # with regard to binary text blocks that can switch back to line mode. It also
33
+ # permits the line-delimiter to change in midstream.
34
+ # This was originally written to support Stomp.
35
+
36
+ # TODO! We're not enforcing the limits on header lengths and text-lengths.
37
+ # When we get around to that, call #receive_error if the user defined it, otherwise
38
+ # throw exceptions.
39
+
40
+ module EventMachine
41
+ module Protocols
42
+ module LineText2
43
+ MaxLineLength = 16*1024
44
+ MaxBinaryLength = 32*1024*1024
45
+
46
+ #--
47
+ # Will be called recursively until there's no data to read.
48
+ # That way the user-defined handlers we call can modify the
49
+ # handling characteristics on a per-token basis.
50
+ #
51
+ def receive_data data
52
+ return unless (data and data.length > 0)
53
+
54
+ # Do this stuff in lieu of a constructor.
55
+ @lt2_mode ||= :lines
56
+ @lt2_delimiter ||= "\n"
57
+ @lt2_linebuffer ||= []
58
+
59
+ if @lt2_mode == :lines
60
+ if ix = data.index( @lt2_delimiter )
61
+ @lt2_linebuffer << data[0...ix]
62
+ ln = @lt2_linebuffer.join
63
+ @lt2_linebuffer.clear
64
+ if @lt2_delimiter == "\n"
65
+ ln.chomp!
66
+ end
67
+ receive_line ln
68
+ receive_data data[(ix+@lt2_delimiter.length)..-1]
69
+ else
70
+ @lt2_linebuffer << data
71
+ end
72
+ elsif @lt2_mode == :text
73
+ if @lt2_textsize
74
+ needed = @lt2_textsize - @lt2_textpos
75
+ will_take = if data.length > needed
76
+ needed
77
+ else
78
+ data.length
79
+ end
80
+
81
+ @lt2_textbuffer << data[0...will_take]
82
+ tail = data[will_take..-1]
83
+
84
+ @lt2_textpos += will_take
85
+ if @lt2_textpos >= @lt2_textsize
86
+ receive_binary_data @lt2_textbuffer.join
87
+ set_line_mode
88
+ end
89
+
90
+ receive_data tail
91
+ else
92
+ receive_binary_data data
93
+ end
94
+ end
95
+ end
96
+
97
+
98
+ def set_delimiter delim
99
+ @lt2_delimiter = delim.to_s
100
+ end
101
+
102
+ # Called internally but also exposed to user code, for the case in which
103
+ # processing of binary data creates a need to transition back to line mode.
104
+ # We support an optional parameter to "throw back" some data, which might
105
+ # be an umprocessed chunk of the transmitted binary data, or something else
106
+ # entirely.
107
+ def set_line_mode data=""
108
+ @lt2_mode = :lines
109
+ (@lt2_linebuffer ||= []).clear
110
+ receive_data data.to_s
111
+ end
112
+
113
+ def set_text_mode size=nil
114
+ if size == 0
115
+ set_line_mode
116
+ else
117
+ @lt2_mode = :text
118
+ (@lt2_textbuffer ||= []).clear
119
+ @lt2_textsize = size # which can be nil, signifying no limit
120
+ @lt2_textpos = 0
121
+ end
122
+ end
123
+
124
+ # In case of a dropped connection, we'll send a partial buffer to user code
125
+ # when in sized text mode. User overrides of #receive_binary_data need to
126
+ # be aware that they may get a short buffer.
127
+ def unbind
128
+ if @lt2_mode == :text and @lt2_textpos > 0
129
+ receive_binary_data @lt2_textbuffer.join
130
+ end
131
+ end
132
+
133
+ # Stub. Should be subclassed by user code.
134
+ def receive_line ln
135
+ # no-op
136
+ end
137
+
138
+ # Stub. Should be subclassed by user code.
139
+ def receive_binary_data data
140
+ # no-op
141
+ end
142
+ end
143
+ end
144
+ end
145
+
@@ -0,0 +1,127 @@
1
+ # $Id: stomp.rb 488 2007-07-30 05:09:40Z blackhedd $
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+
27
+
28
+
29
+ module EventMachine
30
+ module Protocols
31
+
32
+ # Implements Stomp (http://docs.codehaus.org/display/STOMP/Protocol).
33
+ #
34
+ module Stomp
35
+ include LineText2
36
+
37
+ class Message
38
+ attr_accessor :command, :header, :body
39
+ def initialize
40
+ @header = {}
41
+ @state = :precommand
42
+ @content_length = nil
43
+ end
44
+ def consume_line line
45
+ if @state == :precommand
46
+ unless line =~ /\A\s*\Z/
47
+ @command = line
48
+ @state = :headers
49
+ end
50
+ elsif @state == :headers
51
+ if line == ""
52
+ if @content_length
53
+ yield( [:sized_text, @content_length+1] )
54
+ else
55
+ @state = :body
56
+ yield( [:unsized_text] )
57
+ end
58
+ elsif line =~ /\A([^:]+):(.+)\Z/
59
+ k = $1.dup.strip
60
+ v = $2.dup.strip
61
+ @header[k] = v
62
+ if k == "content-length"
63
+ @content_length = v.to_i
64
+ end
65
+ else
66
+ # This is a protocol error. How to signal it?
67
+ end
68
+ elsif @state == :body
69
+ @body = line
70
+ yield( [:dispatch] )
71
+ end
72
+ end
73
+ end
74
+
75
+ def send_frame verb, headers={}, body=""
76
+ ary = [verb, "\n"]
77
+ headers.each {|k,v| ary << "#{k}:#{v}\n" }
78
+ ary << "content-length: #{body.to_s.length}\n"
79
+ ary << "content-type: text/plain; charset=UTF-8\n"
80
+ ary << "\n"
81
+ ary << body.to_s
82
+ ary << "\0"
83
+ send_data ary.join
84
+ end
85
+
86
+ def receive_line line
87
+ @stomp_initialized || init_message_reader
88
+ @stomp_message.consume_line(line) {|outcome|
89
+ if outcome.first == :sized_text
90
+ set_text_mode outcome[1]
91
+ elsif outcome.first == :unsized_text
92
+ set_delimiter "\0"
93
+ elsif outcome.first == :dispatch
94
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
95
+ init_message_reader
96
+ end
97
+ }
98
+ end
99
+ def receive_binary_data data
100
+ @stomp_message.body = data[0..-2]
101
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
102
+ init_message_reader
103
+ end
104
+
105
+ def init_message_reader
106
+ @stomp_initialized = true
107
+ set_delimiter "\n"
108
+ set_line_mode
109
+ @stomp_message = Message.new
110
+ end
111
+
112
+
113
+ # STOMP verbs
114
+ def connect parms={}
115
+ send_frame "CONNECT", parms
116
+ end
117
+ def send destination, body, parms={}
118
+ send_frame "SEND", parms.merge( :destination=>destination ), body.to_s
119
+ end
120
+ def subscribe dest, ack=false
121
+ send_frame "SUBSCRIBE", {:destination=>dest, :ack=>(ack ? "client" : "auto")}
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+
@@ -1,4 +1,4 @@
1
- # $Id: test_basic.rb 323 2007-05-22 22:22:43Z blackhedd $
1
+ # $Id: test_basic.rb 452 2007-07-21 13:35:16Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -44,6 +44,8 @@ class TestBasic < Test::Unit::TestCase
44
44
  assert_equal( :pure_ruby, lt )
45
45
  when :extension
46
46
  assert_equal( :extension, lt )
47
+ when :java
48
+ assert_equal( :java, lt )
47
49
  else
48
50
  assert_equal( :extension, lt )
49
51
  end
@@ -74,19 +76,19 @@ class TestBasic < Test::Unit::TestCase
74
76
 
75
77
  #-------------------------------------
76
78
 
77
- # This test throws an already-running exception. WHY?
79
+ # This test once threw an already-running exception.
78
80
  module Trivial
79
81
  def post_init
80
- EentMachine.stop
82
+ EventMachine.stop
81
83
  end
82
84
  end
83
85
 
84
86
  def test_server
85
- #EventMachine.run {
86
- # EventMachine.start_server "localhost", 9000, Trivial
87
- # EventMachine.connect "localhost", 9000
88
- #}
89
-
87
+ EventMachine.run {
88
+ EventMachine.start_server "localhost", 9000, Trivial
89
+ EventMachine.connect "localhost", 9000
90
+ }
91
+ assert( true ) # make sure it halts
90
92
  end
91
93
 
92
94
  #--------------------------------------
@@ -1,4 +1,4 @@
1
- # $Id: test_epoll.rb 373 2007-06-08 16:09:01Z blackhedd $
1
+ # $Id: test_epoll.rb 449 2007-07-21 00:15:30Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -67,11 +67,14 @@ class TestEpoll < Test::Unit::TestCase
67
67
  # We can set the rlimit/nofile of a process but we can only set it
68
68
  # higher if we're running as root.
69
69
  # On most systems, the default value is 1024.
70
+ # Java doesn't (currently) implement this.
70
71
  def test_rlimit
71
- a = EM.set_descriptor_table_size
72
- assert( a <= 1024 )
73
- a = EM.set_descriptor_table_size( 1024 )
74
- assert( a == 1024 )
72
+ unless RUBY_PLATFORM =~ /java/
73
+ a = EM.set_descriptor_table_size
74
+ assert( a <= 1024 )
75
+ a = EM.set_descriptor_table_size( 1024 )
76
+ assert( a == 1024 )
77
+ end
75
78
  end
76
79
 
77
80
  # Run a high-volume version of this test by kicking the number of connections
@@ -1,4 +1,4 @@
1
- # $Id: test_ltp.rb 396 2007-07-11 01:49:34Z blackhedd $
1
+ # $Id: test_ltp.rb 448 2007-07-21 00:15:05Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -43,6 +43,9 @@ class TestLineAndTextProtocol < Test::Unit::TestCase
43
43
  end
44
44
 
45
45
  def test_simple_lines
46
+ # THIS TEST CURRENTLY FAILS IN JRUBY.
47
+ assert( RUBY_PLATFORM !~ /java/ )
48
+
46
49
  lines_received = []
47
50
  Thread.abort_on_exception = true
48
51
  EventMachine.run {
@@ -72,6 +75,9 @@ class TestLineAndTextProtocol < Test::Unit::TestCase
72
75
  end
73
76
 
74
77
  def test_overlength_lines
78
+ # THIS TEST CURRENTLY FAILS IN JRUBY.
79
+ assert( RUBY_PLATFORM !~ /java/ )
80
+
75
81
  lines_received = []
76
82
  Thread.abort_on_exception = true
77
83
  EventMachine.run {
@@ -0,0 +1,260 @@
1
+ # $Id: test_ltp2.rb 486 2007-07-29 17:15:12Z blackhedd $
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 8 April 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+ #
27
+
28
+ $:.unshift "../lib"
29
+ require 'eventmachine'
30
+
31
+ # TODO!!! Need tests for overlength headers and text bodies.
32
+
33
+ class TestLineText2 < Test::Unit::TestCase
34
+
35
+ # Run each of these tests two ways: passing in the whole test-dataset in one chunk,
36
+ # and passing it in one character at a time.
37
+
38
+ class Basic
39
+ include EM::Protocols::LineText2
40
+ attr_reader :lines
41
+ def receive_line line
42
+ (@lines ||= []) << line
43
+ end
44
+ end
45
+ def test_basic
46
+ testdata = "Line 1\nLine 2\r\nLine 3\n"
47
+
48
+ a = Basic.new
49
+ a.receive_data testdata
50
+ assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
51
+
52
+ a = Basic.new
53
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
54
+ assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
55
+ end
56
+
57
+
58
+ class ChangeDelimiter
59
+ include EM::Protocols::LineText2
60
+ attr_reader :lines
61
+ def initialize *args
62
+ super
63
+ @delim = "A"
64
+ set_delimiter @delim
65
+ end
66
+ def receive_line line
67
+ (@lines ||= []) << line
68
+ set_delimiter( @delim.succ! )
69
+ end
70
+ end
71
+
72
+ def test_change_delimiter
73
+ testdata = %Q(LineaALinebBLinecCLinedD)
74
+
75
+ a = ChangeDelimiter.new
76
+ a.receive_data testdata
77
+ assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
78
+
79
+ a = ChangeDelimiter.new
80
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
81
+ assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
82
+ end
83
+
84
+
85
+ #--
86
+ # Test two lines followed by an empty line, ten bytes of binary data, then
87
+ # two more lines.
88
+
89
+ class Binary
90
+ include EM::Protocols::LineText2
91
+ attr_reader :lines, :body
92
+ def initialize *args
93
+ super
94
+ @lines = []
95
+ @body = nil
96
+ end
97
+ def receive_line ln
98
+ if ln == ""
99
+ set_text_mode 10
100
+ else
101
+ @lines << ln
102
+ end
103
+ end
104
+ def receive_binary_data data
105
+ @body = data
106
+ end
107
+ end
108
+
109
+ def test_binary
110
+ testdata = %Q(Line 1
111
+ Line 2
112
+
113
+ 0000000000Line 3
114
+ Line 4
115
+ )
116
+
117
+ a = Binary.new
118
+ a.receive_data testdata
119
+ assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)
120
+ assert_equal( "0000000000", a.body )
121
+
122
+ a = Binary.new
123
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
124
+ assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)
125
+ assert_equal( "0000000000", a.body )
126
+ end
127
+
128
+
129
+ # Test unsized binary data. The expectation is that each chunk of it
130
+ # will be passed to us as it it received.
131
+ class UnsizedBinary
132
+ include EM::Protocols::LineText2
133
+ attr_reader :n_calls, :body
134
+ def initialize *args
135
+ super
136
+ set_text_mode
137
+ end
138
+ def receive_binary_data data
139
+ @n_calls ||= 0
140
+ @n_calls += 1
141
+ (@body ||= "") << data
142
+ end
143
+ end
144
+
145
+ def test_unsized_binary
146
+ testdata = "X\0" * 1000
147
+
148
+ a = UnsizedBinary.new
149
+ a.receive_data testdata
150
+ assert_equal( 1, a.n_calls )
151
+ assert_equal( testdata, a.body )
152
+
153
+ a = UnsizedBinary.new
154
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
155
+ assert_equal( 2000, a.n_calls )
156
+ assert_equal( testdata, a.body )
157
+ end
158
+
159
+
160
+ # Test binary data with a "throw back" into line-mode.
161
+ class ThrowBack
162
+ include EM::Protocols::LineText2
163
+ attr_reader :headers
164
+ def initialize *args
165
+ super
166
+ @headers = []
167
+ @n_bytes = 0
168
+ set_text_mode
169
+ end
170
+ def receive_binary_data data
171
+ wanted = 25 - @n_bytes
172
+ will_take = if data.length > wanted
173
+ data.length - wanted
174
+ else
175
+ data.length
176
+ end
177
+ @n_bytes += will_take
178
+
179
+ if @n_bytes == 25
180
+ set_line_mode( data[will_take..-1] )
181
+ end
182
+ end
183
+ def receive_line ln
184
+ @headers << ln
185
+ end
186
+ end
187
+ def test_throw_back
188
+ testdata = "Line\n" * 10
189
+
190
+ a = ThrowBack.new
191
+ a.receive_data testdata
192
+ assert_equal( ["Line"] * 5, a.headers )
193
+
194
+ a = ThrowBack.new
195
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
196
+ assert_equal( ["Line"] * 5, a.headers )
197
+ end
198
+
199
+ # Test multi-character line delimiters.
200
+ # Also note that the test data has a "tail" with no delimiter, that will be
201
+ # discarded, but cf. the BinaryTail test.
202
+ # TODO!!! This test doesn't work in the byte-by-byte case.
203
+ class Multichar
204
+ include EM::Protocols::LineText2
205
+ attr_reader :lines
206
+ def initialize *args
207
+ super
208
+ @lines = []
209
+ set_delimiter "012"
210
+ end
211
+ def receive_line ln
212
+ @lines << ln
213
+ end
214
+ end
215
+ def test_multichar
216
+ testdata = "Line012Line012Line012Line"
217
+
218
+ a = Multichar.new
219
+ a.receive_data testdata
220
+ assert_equal( ["Line"]*3, a.lines )
221
+
222
+ a = Multichar.new
223
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
224
+ # DOESN'T WORK in this case. Multi-character delimiters are broken.
225
+ #assert_equal( ["Line"]*3, a.lines )
226
+ end
227
+
228
+ # Test a binary "tail," when a sized binary transfer doesn't complete because
229
+ # of an unbind. We get a partial result.
230
+ class BinaryTail
231
+ include EM::Protocols::LineText2
232
+ attr_reader :data
233
+ def initialize *args
234
+ super
235
+ @data = ""
236
+ set_text_mode 1000
237
+ end
238
+ def receive_binary_data data
239
+ # we expect to get all the data in one chunk, even in the byte-by-byte case,
240
+ # because sized transfers by definition give us exactly one call to
241
+ # #receive_binary_data.
242
+ @data = data
243
+ end
244
+ end
245
+ def test_binary_tail
246
+ testdata = "0" * 500
247
+
248
+ a = BinaryTail.new
249
+ a.receive_data testdata
250
+ a.unbind
251
+ assert_equal( "0" * 500, a.data )
252
+
253
+ a = BinaryTail.new
254
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
255
+ a.unbind
256
+ assert_equal( "0" * 500, a.data )
257
+ end
258
+
259
+ end
260
+