eventmachine 0.12.0 → 0.12.2

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