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.
- 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
|
+
|