eventmachine 0.12.0 → 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.
@@ -1,4 +1,4 @@
1
- # $Id: eventmachine_version.rb 686 2008-05-14 21:21:10Z francis $
1
+ # $Id: eventmachine_version.rb 785 2008-09-15 09:46:23Z francis $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -25,7 +25,7 @@
25
25
 
26
26
  module EventMachine
27
27
 
28
- VERSION = "0.12.0"
28
+ VERSION = "0.12.2"
29
29
 
30
30
  end
31
31
 
@@ -1,4 +1,4 @@
1
- # $Id: jeventmachine.rb 668 2008-01-04 23:00:34Z blackhedd $
1
+ # $Id: jeventmachine.rb 771 2008-08-28 00:45:23Z francis $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -41,14 +41,16 @@ module EventMachine
41
41
  ConnectionCompleted = 104
42
42
  LoopbreakSignalled = 105
43
43
 
44
- class EM < com.rubyeventmachine.EmReactor
44
+ # This thunk class used to be called EM, but that caused conflicts with
45
+ # the alias "EM" for module EventMachine. (FC, 20Jun08)
46
+ class JEM < com.rubyeventmachine.EmReactor
45
47
  def eventCallback a1, a2, a3
46
48
  s = String.from_java_bytes(a3.array[a3.position...a3.limit])
47
49
  EventMachine::event_callback a1, a2, s
48
50
  end
49
51
  end
50
52
  def self.initialize_event_machine
51
- @em = EM.new
53
+ @em = JEM.new
52
54
  end
53
55
  def self.release_machine
54
56
  @em = nil
@@ -68,8 +70,12 @@ module EventMachine
68
70
  def self.stop_tcp_server sig
69
71
  @em.stopTcpServer sig
70
72
  end
73
+ def self.start_unix_server filename
74
+ # TEMPORARILY unsupported until someone figures out how to do it.
75
+ raise "unsupported on this platform"
76
+ end
71
77
  def self.send_data sig, data, length
72
- @em.sendData sig, data, length
78
+ @em.sendData sig, data.to_java_bytes
73
79
  end
74
80
  def self.send_datagram sig, data, length, address, port
75
81
  @em.sendDatagram sig, data, length, address, port
@@ -80,6 +86,9 @@ module EventMachine
80
86
  def self.close_connection sig, after_writing
81
87
  @em.closeConnection sig, after_writing
82
88
  end
89
+ def self.set_comm_inactivity_timeout sig, interval
90
+ @em.setCommInactivityTimeout sig, interval
91
+ end
83
92
  def self.start_tls sig
84
93
  @em.startTls sig
85
94
  end
@@ -99,8 +108,25 @@ module EventMachine
99
108
  def self.open_udp_socket server, port
100
109
  @em.openUdpSocket server, port
101
110
  end
111
+ def self.invoke_popen cmd
112
+ # TEMPORARILY unsupported until someone figures out how to do it.
113
+ raise "unsupported on this platform"
114
+ end
115
+ def self.read_keyboard
116
+ # TEMPORARILY unsupported until someone figures out how to do it.
117
+ raise "temporarily unsupported on this platform"
118
+ end
119
+ def self.set_max_timer_count num
120
+ # harmless no-op in Java. There's no built-in timer limit.
121
+ end
102
122
  def self.library_type
103
123
  :java
104
124
  end
125
+
126
+ class Connection
127
+ def associate_callback_target sig
128
+ # No-op for the time being
129
+ end
130
+ end
105
131
  end
106
132
 
@@ -1,4 +1,4 @@
1
- # $Id: header_and_content.rb 668 2008-01-04 23:00:34Z blackhedd $
1
+ # $Id: header_and_content.rb 782 2008-09-13 20:02:17Z francis $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -28,7 +28,13 @@
28
28
  module EventMachine
29
29
  module Protocols
30
30
 
31
- class HeaderAndContentProtocol < LineAndTextProtocol
31
+ # Originally, this subclassed LineAndTextProtocol, which in
32
+ # turn relies on BufferedTokenizer, which doesn't gracefully
33
+ # handle the transitions between lines and binary text.
34
+ # Changed 13Sep08 by FCianfrocca.
35
+ class HeaderAndContentProtocol < Connection
36
+ include LineText2
37
+
32
38
 
33
39
  ContentLengthPattern = /Content-length:\s*(\d+)/i
34
40
 
@@ -1,4 +1,4 @@
1
- # $Id: httpclient.rb 668 2008-01-04 23:00:34Z blackhedd $
1
+ # $Id: httpclient.rb 786 2008-09-16 07:33:27Z francis $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -109,6 +109,8 @@ class HttpClient < Connection
109
109
  qs = "?" + qs
110
110
  end
111
111
 
112
+ version = args[:version] || "1.1"
113
+
112
114
  # Allow an override for the host header if it's not the connect-string.
113
115
  host = args[:host_header] || args[:host] || "_"
114
116
  # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
@@ -122,7 +124,7 @@ class HttpClient < Connection
122
124
  # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
123
125
  # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
124
126
  req = [
125
- "#{verb} #{request}#{qs} HTTP/1.1",
127
+ "#{verb} #{request}#{qs} HTTP/#{version}",
126
128
  "Host: #{host}:#{port}",
127
129
  "User-agent: Ruby EventMachine",
128
130
  ]
@@ -138,6 +140,12 @@ class HttpClient < Connection
138
140
  req << "Cookie: #{args[:cookie]}"
139
141
  end
140
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
+
141
149
  req << ""
142
150
  reqstring = req.map {|l| "#{l}\r\n"}.join
143
151
  send_data reqstring
@@ -159,12 +167,13 @@ class HttpClient < Connection
159
167
  @content = ""
160
168
  @status = nil
161
169
  @read_state = :header
170
+ @connection_close = nil
162
171
  when :header
163
172
  ary = data.split( /\r?\n/m, 2 )
164
173
  if ary.length == 2
165
174
  data = ary.last
166
175
  if ary.first == ""
167
- if @content_length and @content_length > 0
176
+ if (@content_length and @content_length > 0) || @connection_close
168
177
  @read_state = :content
169
178
  else
170
179
  dispatch_response
@@ -182,6 +191,8 @@ class HttpClient < Connection
182
191
  # a bad guy. (There is an exploit that depends on multiple
183
192
  # content-length headers.)
184
193
  @content_length ||= $'.to_i
194
+ elsif ary.first =~ /\Aconnection:\s*close/i
195
+ @connection_close = true
185
196
  end
186
197
  end
187
198
  else
@@ -1,4 +1,4 @@
1
- # $Id: linetext2.rb 668 2008-01-04 23:00:34Z blackhedd $
1
+ # $Id: linetext2.rb 778 2008-09-13 19:54:18Z francis $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -83,8 +83,13 @@ module EventMachine
83
83
 
84
84
  @lt2_textpos += will_take
85
85
  if @lt2_textpos >= @lt2_textsize
86
- receive_binary_data @lt2_textbuffer.join
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).
87
90
  set_line_mode
91
+ receive_binary_data @lt2_textbuffer.join
92
+ receive_end_of_binary_data
88
93
  end
89
94
 
90
95
  receive_data tail
@@ -121,6 +126,11 @@ module EventMachine
121
126
  end
122
127
  end
123
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
+
124
134
  # In case of a dropped connection, we'll send a partial buffer to user code
125
135
  # when in sized text mode. User overrides of #receive_binary_data need to
126
136
  # be aware that they may get a short buffer.
@@ -139,6 +149,14 @@ module EventMachine
139
149
  def receive_binary_data data
140
150
  # no-op
141
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
142
160
  end
143
161
  end
144
162
  end
@@ -0,0 +1,261 @@
1
+ #
2
+ # $Id: postgres.rb 783 2008-09-14 02:48:46Z francis $
3
+ #
4
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
5
+ # Homepage:: http://rubyeventmachine.com
6
+ # Date:: 15 November 2006
7
+ #
8
+ # See EventMachine and EventMachine::Connection for documentation and
9
+ # usage examples.
10
+ #
11
+ #----------------------------------------------------------------------------
12
+ #
13
+ # Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
14
+ # Gmail: blackhedd
15
+ #
16
+ # This program is free software; you can redistribute it and/or modify
17
+ # it under the terms of either: 1) the GNU General Public License
18
+ # as published by the Free Software Foundation; either version 2 of the
19
+ # License, or (at your option) any later version; or 2) Ruby's License.
20
+ #
21
+ # See the file COPYING for complete licensing information.
22
+ #
23
+ #---------------------------------------------------------------------------
24
+ #
25
+ #
26
+ #
27
+
28
+
29
+ =begin
30
+ PROVISIONAL IMPLEMENTATION of an evented Postgres client.
31
+ This implements version 3 of the Postgres wire protocol, which will work
32
+ with any Postgres version from roughly 7.4 onward.
33
+
34
+ Until this code is judged ready for prime time, you have to access it by
35
+ explicitly requiring protocols/postgres.
36
+
37
+ Objective: we want to access Postgres databases without requiring threads.
38
+ Until now this has been a problem because the Postgres client implementations
39
+ have all made use of blocking I/O calls, which is incompatible with a
40
+ thread-free evented model.
41
+
42
+ But rather than re-implement the Postgres Wire3 protocol, we're taking advantage
43
+ of the existing postgres-pr library, which was originally written by Michael
44
+ Neumann but (at this writing) appears to be no longer maintained. Still, it's
45
+ in basically a production-ready state, and the wire protocol isn't that complicated
46
+ anyway.
47
+
48
+ We need to monkeypatch StringIO because it lacks the #readbytes method needed
49
+ by postgres-pr.
50
+
51
+ We're tucking in a bunch of require statements that may not be present in garden-variety
52
+ EM installations. Until we find a good way to only require these if a program
53
+ requires postgres, this file will need to be required explicitly.
54
+
55
+ The StringIO monkeypatch is lifted verbatim from the standard library readbytes.rb,
56
+ which adds method #readbytes directly to class IO. But StringIO is not a subclass of IO.
57
+
58
+ We cloned the handling of postgres messages from lib/postgres-pr/connection.rb
59
+ in the postgres-pr library, and modified it for event-handling.
60
+
61
+ TODO: The password handling in dispatch_conn_message is totally incomplete.
62
+
63
+
64
+ We return Deferrables from the user-level operations surfaced by this interface.
65
+ Experimentally, we're using the pattern of always returning a boolean value as the
66
+ first argument of a deferrable callback to indicate success or failure. This is
67
+ instead of the traditional pattern of calling Deferrable#succeed or #fail, and
68
+ requiring the user to define both a callback and an errback function.
69
+
70
+ Sample code:
71
+ require 'eventmachine'
72
+ require 'protocols/postgres' # provisionally needed
73
+
74
+ EM.run {
75
+ db = EM.connect_unix_domain( "/tmp/.s.PGSQL.5432", EM::P::Postgres3 )
76
+ db.connect( dbname, username, psw ).callback do |status|
77
+ if status
78
+ db.query( "select * from some_table" ).callback do |status, result, errors|
79
+ if status
80
+ result.rows.each do |row|
81
+ p row
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ }
88
+
89
+
90
+
91
+ =end
92
+
93
+
94
+ require 'readbytes'
95
+ require 'postgres-pr/message'
96
+ require 'postgres-pr/connection'
97
+ require 'stringio'
98
+
99
+ include PostgresPR
100
+
101
+ class StringIO
102
+ # Reads exactly +n+ bytes.
103
+ #
104
+ # If the data read is nil an EOFError is raised.
105
+ #
106
+ # If the data read is too short a TruncatedDataError is raised and the read
107
+ # data is obtainable via its #data method.
108
+ def readbytes(n)
109
+ str = read(n)
110
+ if str == nil
111
+ raise EOFError, "End of file reached"
112
+ end
113
+ if str.size < n
114
+ raise TruncatedDataError.new("data truncated", str)
115
+ end
116
+ str
117
+ end
118
+ end
119
+
120
+
121
+ module EventMachine; module Protocols; class Postgres3 < EventMachine::Connection
122
+
123
+
124
+ def initialize
125
+ @data = ""
126
+ @params = {}
127
+ end
128
+
129
+ def connect db, user, psw=nil
130
+ d = EM::DefaultDeferrable.new
131
+ d.timeout 15
132
+
133
+ if @pending_query || @pending_conn
134
+ d.succeed false, "Operation already in progress"
135
+ else
136
+ @pending_conn = d
137
+ prms = {"user"=>user, "database"=>db}
138
+ @user = user
139
+ if psw
140
+ @password = psw
141
+ #prms["password"] = psw
142
+ end
143
+ send_data PostgresPR::StartupMessage.new( 3 << 16, prms ).dump
144
+ end
145
+
146
+ d
147
+ end
148
+
149
+ def query sql
150
+ d = EM::DefaultDeferrable.new
151
+ d.timeout 15
152
+
153
+ if @pending_query || @pending_conn
154
+ d.succeed false, "Operation already in progress"
155
+ else
156
+ @r = PostgresPR::Connection::Result.new
157
+ @e = []
158
+ @pending_query = d
159
+ send_data PostgresPR::Query.dump(sql)
160
+ end
161
+
162
+ d
163
+ end
164
+
165
+
166
+ def receive_data data
167
+ @data << data
168
+ while @data.length >= 5
169
+ pktlen = @data[1...5].unpack("N").first
170
+ if @data.length >= (1 + pktlen)
171
+ pkt = @data.slice!(0...(1+pktlen))
172
+ m = StringIO.open( pkt, "r" ) {|io| PostgresPR::Message.read( io ) }
173
+ if @pending_conn
174
+ dispatch_conn_message m
175
+ elsif @pending_query
176
+ dispatch_query_message m
177
+ else
178
+ raise "Unexpected message from database"
179
+ end
180
+ else
181
+ break # very important, break out of the while
182
+ end
183
+ end
184
+ end
185
+
186
+
187
+ def unbind
188
+ if o = (@pending_query || @pending_conn)
189
+ o.succeed false, "lost connection"
190
+ end
191
+ end
192
+
193
+ # Cloned and modified from the postgres-pr.
194
+ def dispatch_conn_message msg
195
+ case msg
196
+ when AuthentificationClearTextPassword
197
+ raise ArgumentError, "no password specified" if @password.nil?
198
+ send_data PasswordMessage.new(@password).dump
199
+
200
+ when AuthentificationCryptPassword
201
+ raise ArgumentError, "no password specified" if @password.nil?
202
+ send_data PasswordMessage.new(@password.crypt(msg.salt)).dump
203
+
204
+ when AuthentificationMD5Password
205
+ raise ArgumentError, "no password specified" if @password.nil?
206
+ require 'digest/md5'
207
+
208
+ m = Digest::MD5.hexdigest(@password + @user)
209
+ m = Digest::MD5.hexdigest(m + msg.salt)
210
+ m = 'md5' + m
211
+ send_data PasswordMessage.new(m).dump
212
+
213
+ when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential
214
+ raise "unsupported authentification"
215
+
216
+ when AuthentificationOk
217
+ when ErrorResponse
218
+ raise msg.field_values.join("\t")
219
+ when NoticeResponse
220
+ @notice_processor.call(msg) if @notice_processor
221
+ when ParameterStatus
222
+ @params[msg.key] = msg.value
223
+ when BackendKeyData
224
+ # TODO
225
+ #p msg
226
+ when ReadyForQuery
227
+ # TODO: use transaction status
228
+ pc,@pending_conn = @pending_conn,nil
229
+ pc.succeed true
230
+ else
231
+ raise "unhandled message type"
232
+ end
233
+ end
234
+
235
+ # Cloned and modified from the postgres-pr.
236
+ def dispatch_query_message msg
237
+ case msg
238
+ when DataRow
239
+ @r.rows << msg.columns
240
+ when CommandComplete
241
+ @r.cmd_tag = msg.cmd_tag
242
+ when ReadyForQuery
243
+ pq,@pending_query = @pending_query,nil
244
+ pq.succeed true, @r, @e
245
+ when RowDescription
246
+ @r.fields = msg.fields
247
+ when CopyInResponse
248
+ when CopyOutResponse
249
+ when EmptyQueryResponse
250
+ when ErrorResponse
251
+ # TODO
252
+ @e << msg
253
+ when NoticeResponse
254
+ @notice_processor.call(msg) if @notice_processor
255
+ else
256
+ # TODO
257
+ end
258
+ end
259
+
260
+ end; end; end
261
+