brianmario-eventmachine 0.12.2

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.
Files changed (72) hide show
  1. data/COPYING +60 -0
  2. data/DEFERRABLES +138 -0
  3. data/EPOLL +141 -0
  4. data/GNU +281 -0
  5. data/KEYBOARD +38 -0
  6. data/LEGAL +25 -0
  7. data/LIGHTWEIGHT_CONCURRENCY +72 -0
  8. data/PURE_RUBY +77 -0
  9. data/README +74 -0
  10. data/RELEASE_NOTES +96 -0
  11. data/SMTP +9 -0
  12. data/SPAWNED_PROCESSES +93 -0
  13. data/TODO +10 -0
  14. data/eventmachine.gemspec +15 -0
  15. data/ext/binder.cpp +126 -0
  16. data/ext/binder.h +48 -0
  17. data/ext/cmain.cpp +553 -0
  18. data/ext/cplusplus.cpp +172 -0
  19. data/ext/ed.cpp +1473 -0
  20. data/ext/ed.h +361 -0
  21. data/ext/em.cpp +1890 -0
  22. data/ext/em.h +170 -0
  23. data/ext/emwin.cpp +300 -0
  24. data/ext/emwin.h +94 -0
  25. data/ext/epoll.cpp +26 -0
  26. data/ext/epoll.h +25 -0
  27. data/ext/eventmachine.h +90 -0
  28. data/ext/eventmachine_cpp.h +94 -0
  29. data/ext/extconf.rb +203 -0
  30. data/ext/files.cpp +94 -0
  31. data/ext/files.h +65 -0
  32. data/ext/kb.cpp +368 -0
  33. data/ext/page.cpp +107 -0
  34. data/ext/page.h +51 -0
  35. data/ext/pipe.cpp +327 -0
  36. data/ext/project.h +119 -0
  37. data/ext/rubymain.cpp +678 -0
  38. data/ext/sigs.cpp +89 -0
  39. data/ext/sigs.h +32 -0
  40. data/ext/ssl.cpp +408 -0
  41. data/ext/ssl.h +86 -0
  42. data/lib/em/deferrable.rb +208 -0
  43. data/lib/em/eventable.rb +39 -0
  44. data/lib/em/future.rb +62 -0
  45. data/lib/em/messages.rb +66 -0
  46. data/lib/em/processes.rb +68 -0
  47. data/lib/em/spawnable.rb +88 -0
  48. data/lib/em/streamer.rb +112 -0
  49. data/lib/eventmachine.rb +1756 -0
  50. data/lib/eventmachine_version.rb +31 -0
  51. data/lib/evma.rb +32 -0
  52. data/lib/evma/callback.rb +32 -0
  53. data/lib/evma/container.rb +75 -0
  54. data/lib/evma/factory.rb +77 -0
  55. data/lib/evma/protocol.rb +87 -0
  56. data/lib/evma/reactor.rb +48 -0
  57. data/lib/jeventmachine.rb +132 -0
  58. data/lib/pr_eventmachine.rb +1011 -0
  59. data/lib/protocols/buftok.rb +127 -0
  60. data/lib/protocols/header_and_content.rb +129 -0
  61. data/lib/protocols/httpcli2.rb +784 -0
  62. data/lib/protocols/httpclient.rb +264 -0
  63. data/lib/protocols/line_and_text.rb +122 -0
  64. data/lib/protocols/linetext2.rb +163 -0
  65. data/lib/protocols/postgres.rb +261 -0
  66. data/lib/protocols/saslauth.rb +179 -0
  67. data/lib/protocols/smtpclient.rb +308 -0
  68. data/lib/protocols/smtpserver.rb +543 -0
  69. data/lib/protocols/stomp.rb +130 -0
  70. data/lib/protocols/tcptest.rb +57 -0
  71. data/setup.rb +1585 -0
  72. metadata +126 -0
@@ -0,0 +1,264 @@
1
+ # $Id$
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 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
+ module EventMachine
29
+ module Protocols
30
+
31
+ class HttpClient < Connection
32
+ include EventMachine::Deferrable
33
+
34
+
35
+ MaxPostContentLength = 20 * 1024 * 1024
36
+
37
+ # USAGE SAMPLE:
38
+ #
39
+ # EventMachine.run {
40
+ # http = EventMachine::Protocols::HttpClient.request(
41
+ # :host => server,
42
+ # :port => 80,
43
+ # :request => "/index.html",
44
+ # :query_string => "parm1=value1&parm2=value2"
45
+ # )
46
+ # http.callback {|response|
47
+ # puts response[:status]
48
+ # puts response[:headers]
49
+ # puts response[:content]
50
+ # }
51
+ # }
52
+ #
53
+
54
+ # TODO:
55
+ # Add streaming so we can support enormous POSTs. Current max is 20meg.
56
+ # Timeout for connections that run too long or hang somewhere in the middle.
57
+ # Persistent connections (HTTP/1.1), may need a associated delegate object.
58
+ # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
59
+ # DNS lookups are unbelievably slow.
60
+ # HEAD requests.
61
+ # Chunked transfer encoding.
62
+ # Convenience methods for requests. get, post, url, etc.
63
+ # SSL.
64
+ # Handle status codes like 304, 100, etc.
65
+ # Refactor this code so that protocol errors all get handled one way (an exception?),
66
+ # instead of sprinkling set_deferred_status :failed calls everywhere.
67
+
68
+ def self.request( args = {} )
69
+ args[:port] ||= 80
70
+ EventMachine.connect( args[:host], args[:port], self ) {|c|
71
+ # According to the docs, we will get here AFTER post_init is called.
72
+ c.instance_eval {@args = args}
73
+ }
74
+ end
75
+
76
+ def post_init
77
+ @start_time = Time.now
78
+ @data = ""
79
+ @read_state = :base
80
+ end
81
+
82
+ # We send the request when we get a connection.
83
+ # AND, we set an instance variable to indicate we passed through here.
84
+ # That allows #unbind to know whether there was a successful connection.
85
+ # NB: This naive technique won't work when we have to support multiple
86
+ # requests on a single connection.
87
+ def connection_completed
88
+ @connected = true
89
+ send_request @args
90
+ end
91
+
92
+ def send_request args
93
+ args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
94
+ args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
95
+
96
+ verb = args[:verb].to_s.upcase
97
+ unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
98
+ set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
99
+ return # NOTE THE EARLY RETURN, we're not sending any data.
100
+ end
101
+
102
+ request = args[:request] || "/"
103
+ unless request[0,1] == "/"
104
+ request = "/" + request
105
+ end
106
+
107
+ qs = args[:query_string] || ""
108
+ if qs.length > 0 and qs[0,1] != '?'
109
+ qs = "?" + qs
110
+ end
111
+
112
+ version = args[:version] || "1.1"
113
+
114
+ # Allow an override for the host header if it's not the connect-string.
115
+ host = args[:host_header] || args[:host] || "_"
116
+ # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
117
+ port = args[:port]
118
+
119
+ # POST items.
120
+ postcontenttype = args[:contenttype] || "application/octet-stream"
121
+ postcontent = args[:content] || ""
122
+ raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
123
+
124
+ # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
125
+ # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
126
+ req = [
127
+ "#{verb} #{request}#{qs} HTTP/#{version}",
128
+ "Host: #{host}:#{port}",
129
+ "User-agent: Ruby EventMachine",
130
+ ]
131
+
132
+ if verb == "POST" || verb == "PUT"
133
+ req << "Content-type: #{postcontenttype}"
134
+ req << "Content-length: #{postcontent.length}"
135
+ end
136
+
137
+ # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
138
+ # Eventually we will want to deal intelligently with arrays and hashes.
139
+ if args[:cookie]
140
+ req << "Cookie: #{args[:cookie]}"
141
+ end
142
+
143
+ # Basic-auth stanza contributed by Mike Murphy.
144
+ if args[:basic_auth]
145
+ basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip
146
+ req << "Authorization: Basic #{basic_auth_string}"
147
+ end
148
+
149
+ req << ""
150
+ reqstring = req.map {|l| "#{l}\r\n"}.join
151
+ send_data reqstring
152
+
153
+ if verb == "POST" || verb == "PUT"
154
+ send_data postcontent
155
+ end
156
+ end
157
+
158
+
159
+ def receive_data data
160
+ while data and data.length > 0
161
+ case @read_state
162
+ when :base
163
+ # Perform any per-request initialization here and don't consume any data.
164
+ @data = ""
165
+ @headers = []
166
+ @content_length = nil # not zero
167
+ @content = ""
168
+ @status = nil
169
+ @read_state = :header
170
+ @connection_close = nil
171
+ when :header
172
+ ary = data.split( /\r?\n/m, 2 )
173
+ if ary.length == 2
174
+ data = ary.last
175
+ if ary.first == ""
176
+ if (@content_length and @content_length > 0) || @connection_close
177
+ @read_state = :content
178
+ else
179
+ dispatch_response
180
+ @read_state = :base
181
+ end
182
+ else
183
+ @headers << ary.first
184
+ if @headers.length == 1
185
+ parse_response_line
186
+ elsif ary.first =~ /\Acontent-length:\s*/i
187
+ # Only take the FIRST content-length header that appears,
188
+ # which we can distinguish because @content_length is nil.
189
+ # TODO, it's actually a fatal error if there is more than one
190
+ # content-length header, because the caller is presumptively
191
+ # a bad guy. (There is an exploit that depends on multiple
192
+ # content-length headers.)
193
+ @content_length ||= $'.to_i
194
+ elsif ary.first =~ /\Aconnection:\s*close/i
195
+ @connection_close = true
196
+ end
197
+ end
198
+ else
199
+ @data << data
200
+ data = ""
201
+ end
202
+ when :content
203
+ # If there was no content-length header, we have to wait until the connection
204
+ # closes. Everything we get until that point is content.
205
+ # TODO: Must impose a content-size limit, and also must implement chunking.
206
+ # Also, must support either temporary files for large content, or calling
207
+ # a content-consumer block supplied by the user.
208
+ if @content_length
209
+ bytes_needed = @content_length - @content.length
210
+ @content += data[0, bytes_needed]
211
+ data = data[bytes_needed..-1] || ""
212
+ if @content_length == @content.length
213
+ dispatch_response
214
+ @read_state = :base
215
+ end
216
+ else
217
+ @content << data
218
+ data = ""
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ # We get called here when we have received an HTTP response line.
226
+ # It's an opportunity to throw an exception or trigger other exceptional
227
+ # handling.
228
+ def parse_response_line
229
+ if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
230
+ @status = $1.to_i
231
+ else
232
+ set_deferred_status :failed, {
233
+ :status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
234
+ }
235
+ close_connection
236
+ end
237
+ end
238
+ private :parse_response_line
239
+
240
+ def dispatch_response
241
+ @read_state = :base
242
+ set_deferred_status :succeeded, {
243
+ :content => @content,
244
+ :headers => @headers,
245
+ :status => @status
246
+ }
247
+ # TODO, we close the connection for now, but this is wrong for persistent clients.
248
+ close_connection
249
+ end
250
+
251
+ def unbind
252
+ if !@connected
253
+ set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
254
+ elsif (@read_state == :content and @content_length == nil)
255
+ dispatch_response
256
+ end
257
+ end
258
+ end
259
+
260
+
261
+ end
262
+ end
263
+
264
+
@@ -0,0 +1,122 @@
1
+ # $Id$
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
+ require File.dirname(__FILE__) + '/buftok'
27
+
28
+ module EventMachine
29
+ module Protocols
30
+
31
+ class LineAndTextProtocol < Connection
32
+ MaxLineLength = 16*1024
33
+ MaxBinaryLength = 32*1024*1024
34
+
35
+ def initialize *args
36
+ super
37
+ lbp_init_line_state
38
+ end
39
+ def receive_data data
40
+ if @lbp_mode == :lines
41
+ begin
42
+ @lpb_buffer.extract(data).each do |line|
43
+ receive_line(line.chomp) if respond_to?(:receive_line)
44
+ end
45
+ rescue Exception
46
+ receive_error('overlength line') if respond_to?(:receive_error)
47
+ close_connection
48
+ return
49
+ end
50
+ else
51
+ if @lbp_binary_limit > 0
52
+ wanted = @lbp_binary_limit - @lbp_binary_bytes_received
53
+ chunk = nil
54
+ if data.length > wanted
55
+ chunk = data.slice!(0...wanted)
56
+ else
57
+ chunk = data
58
+ data = ""
59
+ end
60
+ @lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
61
+ @lbp_binary_bytes_received += chunk.length
62
+ if @lbp_binary_bytes_received == @lbp_binary_limit
63
+ receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
64
+ lbp_init_line_state
65
+ end
66
+ receive_data(data) if data.length > 0
67
+ else
68
+ receive_binary_data(data) if respond_to?(:receive_binary_data)
69
+ data = ""
70
+ end
71
+ end
72
+ end
73
+
74
+ def unbind
75
+ if @lbp_mode == :binary and @lbp_binary_limit > 0
76
+ if respond_to?(:receive_binary_data)
77
+ receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
78
+ end
79
+ end
80
+ end
81
+
82
+ # Set up to read the supplied number of binary bytes.
83
+ # This recycles all the data currently waiting in the line buffer, if any.
84
+ # If the limit is nil, then ALL subsequent data will be treated as binary
85
+ # data and passed to the upstream protocol handler as we receive it.
86
+ # If a limit is given, we'll hold the incoming binary data and not
87
+ # pass it upstream until we've seen it all, or until there is an unbind
88
+ # (in which case we'll pass up a partial).
89
+ # Specifying nil for the limit (the default) means there is no limit.
90
+ # Specifiyng zero for the limit will cause an immediate transition back to line mode.
91
+ #
92
+ def set_binary_mode size = nil
93
+ if @lbp_mode == :lines
94
+ if size == 0
95
+ receive_binary_data("") if respond_to?(:receive_binary_data)
96
+ # Do no more work here. Stay in line mode and keep consuming data.
97
+ else
98
+ @lbp_binary_limit = size.to_i # (nil will be stored as zero)
99
+ if @lbp_binary_limit > 0
100
+ raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
101
+ @lbp_binary_buffer = "\0" * @lbp_binary_limit
102
+ @lbp_binary_bytes_received = 0
103
+ end
104
+
105
+ @lbp_mode = :binary
106
+ receive_data @lpb_buffer.flush
107
+ end
108
+ else
109
+ raise "invalid operation"
110
+ end
111
+ end
112
+
113
+ #--
114
+ # For internal use, establish protocol baseline for handling lines.
115
+ def lbp_init_line_state
116
+ @lpb_buffer = BufferedTokenizer.new("\n", MaxLineLength)
117
+ @lbp_mode = :lines
118
+ end
119
+ private :lbp_init_line_state
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,163 @@
1
+ # $Id$
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
+ # Reset line mode (the default behavior) BEFORE calling the
87
+ # receive_binary_data. This makes it possible for user code
88
+ # to call set_text_mode, enabling chains of text blocks
89
+ # (which can possibly be of different sizes).
90
+ set_line_mode
91
+ receive_binary_data @lt2_textbuffer.join
92
+ receive_end_of_binary_data
93
+ end
94
+
95
+ receive_data tail
96
+ else
97
+ receive_binary_data data
98
+ end
99
+ end
100
+ end
101
+
102
+
103
+ def set_delimiter delim
104
+ @lt2_delimiter = delim.to_s
105
+ end
106
+
107
+ # Called internally but also exposed to user code, for the case in which
108
+ # processing of binary data creates a need to transition back to line mode.
109
+ # We support an optional parameter to "throw back" some data, which might
110
+ # be an umprocessed chunk of the transmitted binary data, or something else
111
+ # entirely.
112
+ def set_line_mode data=""
113
+ @lt2_mode = :lines
114
+ (@lt2_linebuffer ||= []).clear
115
+ receive_data data.to_s
116
+ end
117
+
118
+ def set_text_mode size=nil
119
+ if size == 0
120
+ set_line_mode
121
+ else
122
+ @lt2_mode = :text
123
+ (@lt2_textbuffer ||= []).clear
124
+ @lt2_textsize = size # which can be nil, signifying no limit
125
+ @lt2_textpos = 0
126
+ end
127
+ end
128
+
129
+ # Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
130
+ def set_binary_mode size=nil
131
+ set_text_mode size
132
+ end
133
+
134
+ # In case of a dropped connection, we'll send a partial buffer to user code
135
+ # when in sized text mode. User overrides of #receive_binary_data need to
136
+ # be aware that they may get a short buffer.
137
+ def unbind
138
+ if @lt2_mode == :text and @lt2_textpos > 0
139
+ receive_binary_data @lt2_textbuffer.join
140
+ end
141
+ end
142
+
143
+ # Stub. Should be subclassed by user code.
144
+ def receive_line ln
145
+ # no-op
146
+ end
147
+
148
+ # Stub. Should be subclassed by user code.
149
+ def receive_binary_data data
150
+ # no-op
151
+ end
152
+
153
+ # Stub. Should be subclassed by user code.
154
+ # This is called when transitioning internally from text mode
155
+ # back to line mode. Useful when client code doesn't want
156
+ # to keep track of how much data it's received.
157
+ def receive_end_of_binary_data
158
+ # no-op
159
+ end
160
+ end
161
+ end
162
+ end
163
+