eventmachine 0.12.0 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/cmain.cpp +27 -1
- data/ext/ed.cpp +17 -2
- data/ext/ed.h +9 -1
- data/ext/em.cpp +111 -2
- data/ext/em.h +4 -1
- data/ext/eventmachine.h +9 -2
- data/ext/rubymain.cpp +49 -1
- data/lib/eventmachine.rb +139 -15
- data/lib/eventmachine_version.rb +2 -2
- data/lib/jeventmachine.rb +30 -4
- data/lib/protocols/header_and_content.rb +8 -2
- data/lib/protocols/httpclient.rb +14 -3
- data/lib/protocols/linetext2.rb +20 -2
- data/lib/protocols/postgres.rb +261 -0
- data/lib/protocols/stomp.rb +4 -1
- data/tests/test_attach.rb +66 -0
- data/tests/test_basic.rb +38 -2
- data/tests/test_hc.rb +189 -141
- data/tests/test_httpclient.rb +22 -1
- data/tests/test_ltp2.rb +60 -1
- data/tests/test_next_tick.rb +45 -1
- data/tests/test_sasl.rb +2 -1
- data/tests/test_timers.rb +4 -2
- metadata +63 -61
data/lib/eventmachine_version.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: eventmachine_version.rb
|
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.
|
28
|
+
VERSION = "0.12.2"
|
29
29
|
|
30
30
|
end
|
31
31
|
|
data/lib/jeventmachine.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: jeventmachine.rb
|
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
|
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 =
|
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
|
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
|
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
|
-
|
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
|
|
data/lib/protocols/httpclient.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: httpclient.rb
|
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
|
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
|
data/lib/protocols/linetext2.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $Id: linetext2.rb
|
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
|
-
|
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
|
+
|