eventmachine 0.12.6-x86-mswin32-60 → 0.12.8-x86-mswin32-60
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/{docs/README → README} +21 -13
- data/Rakefile +14 -4
- data/docs/DEFERRABLES +0 -5
- data/docs/INSTALL +2 -4
- data/docs/LEGAL +1 -1
- data/docs/LIGHTWEIGHT_CONCURRENCY +0 -2
- data/docs/PURE_RUBY +0 -2
- data/docs/RELEASE_NOTES +0 -2
- data/docs/SMTP +0 -7
- data/docs/SPAWNED_PROCESSES +0 -4
- data/docs/TODO +0 -2
- data/eventmachine.gemspec +41 -32
- data/examples/ex_channel.rb +43 -0
- data/examples/ex_queue.rb +2 -0
- data/examples/helper.rb +2 -0
- data/ext/cmain.cpp +685 -586
- data/ext/cplusplus.cpp +15 -6
- data/ext/ed.cpp +1732 -1522
- data/ext/ed.h +407 -380
- data/ext/em.cpp +2263 -1937
- data/ext/em.h +223 -186
- data/ext/eventmachine.h +111 -98
- data/ext/eventmachine_cpp.h +1 -0
- data/ext/extconf.rb +4 -0
- data/ext/kb.cpp +81 -82
- data/ext/pipe.cpp +349 -351
- data/ext/project.h +21 -0
- data/ext/rubymain.cpp +1047 -847
- data/ext/ssl.cpp +38 -1
- data/ext/ssl.h +5 -1
- data/java/src/com/rubyeventmachine/Application.java +7 -3
- data/java/src/com/rubyeventmachine/EmReactor.java +16 -1
- data/java/src/com/rubyeventmachine/tests/ConnectTest.java +25 -3
- data/lib/{protocols → em}/buftok.rb +16 -5
- data/lib/em/callback.rb +26 -0
- data/lib/em/channel.rb +57 -0
- data/lib/em/connection.rb +505 -0
- data/lib/em/deferrable.rb +144 -165
- data/lib/em/file_watch.rb +54 -0
- data/lib/em/future.rb +24 -25
- data/lib/em/messages.rb +1 -1
- data/lib/em/process_watch.rb +44 -0
- data/lib/em/processes.rb +119 -113
- data/lib/em/protocols.rb +35 -0
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +263 -0
- data/lib/em/protocols/httpclient2.rb +582 -0
- data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
- data/lib/em/protocols/linetext2.rb +160 -0
- data/lib/{protocols → em/protocols}/memcache.rb +37 -7
- data/lib/em/protocols/object_protocol.rb +39 -0
- data/lib/em/protocols/postgres3.rb +247 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +331 -0
- data/lib/em/protocols/smtpserver.rb +547 -0
- data/lib/em/protocols/stomp.rb +200 -0
- data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
- data/lib/em/queue.rb +61 -0
- data/lib/em/spawnable.rb +53 -56
- data/lib/em/streamer.rb +92 -74
- data/lib/em/timers.rb +55 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1636 -1926
- data/lib/evma.rb +1 -1
- data/lib/jeventmachine.rb +106 -101
- data/lib/pr_eventmachine.rb +47 -36
- data/tasks/project.rake +2 -1
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/test_attach.rb +18 -0
- data/tests/test_basic.rb +285 -231
- data/tests/test_channel.rb +63 -0
- data/tests/test_connection_count.rb +2 -2
- data/tests/test_epoll.rb +162 -163
- data/tests/test_errors.rb +36 -36
- data/tests/test_exc.rb +22 -25
- data/tests/test_file_watch.rb +49 -0
- data/tests/test_futures.rb +77 -93
- data/tests/test_hc.rb +2 -2
- data/tests/test_httpclient.rb +55 -52
- data/tests/test_httpclient2.rb +153 -155
- data/tests/test_inactivity_timeout.rb +30 -0
- data/tests/test_kb.rb +8 -9
- data/tests/test_ltp2.rb +274 -277
- data/tests/test_next_tick.rb +135 -109
- data/tests/test_object_protocol.rb +37 -0
- data/tests/test_process_watch.rb +48 -0
- data/tests/test_processes.rb +128 -95
- data/tests/test_proxy_connection.rb +92 -0
- data/tests/test_pure.rb +1 -5
- data/tests/test_queue.rb +44 -0
- data/tests/test_running.rb +9 -14
- data/tests/test_sasl.rb +32 -34
- data/tests/test_send_file.rb +175 -176
- data/tests/test_servers.rb +37 -41
- data/tests/test_smtpserver.rb +47 -55
- data/tests/test_spawn.rb +284 -291
- data/tests/test_ssl_args.rb +1 -1
- data/tests/test_ssl_methods.rb +1 -1
- data/tests/test_ssl_verify.rb +82 -0
- data/tests/test_timers.rb +81 -88
- data/tests/test_ud.rb +0 -7
- data/tests/testem.rb +1 -1
- metadata +52 -36
- data/lib/em/eventable.rb +0 -39
- data/lib/eventmachine_version.rb +0 -31
- data/lib/protocols/header_and_content.rb +0 -129
- data/lib/protocols/httpcli2.rb +0 -803
- data/lib/protocols/httpclient.rb +0 -270
- data/lib/protocols/linetext2.rb +0 -161
- data/lib/protocols/postgres.rb +0 -261
- data/lib/protocols/saslauth.rb +0 -179
- data/lib/protocols/smtpclient.rb +0 -308
- data/lib/protocols/smtpserver.rb +0 -556
- data/lib/protocols/stomp.rb +0 -153
- data/tests/test_eventables.rb +0 -77
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
#--
|
2
2
|
#
|
3
3
|
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
4
|
# Homepage:: http://rubyeventmachine.com
|
@@ -23,7 +23,7 @@
|
|
23
23
|
#
|
24
24
|
#
|
25
25
|
#
|
26
|
-
require File.dirname(__FILE__) + '
|
26
|
+
require File.dirname(__FILE__) + '/../buftok'
|
27
27
|
|
28
28
|
module EventMachine
|
29
29
|
module Protocols
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#--
|
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
|
+
module EventMachine
|
27
|
+
module Protocols
|
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
|
+
module LineText2
|
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
|
+
MaxLineLength = 16*1024
|
41
|
+
MaxBinaryLength = 32*1024*1024
|
42
|
+
|
43
|
+
#--
|
44
|
+
# Will be called recursively until there's no data to read.
|
45
|
+
# That way the user-defined handlers we call can modify the
|
46
|
+
# handling characteristics on a per-token basis.
|
47
|
+
#
|
48
|
+
def receive_data data
|
49
|
+
return unless (data and data.length > 0)
|
50
|
+
|
51
|
+
# Do this stuff in lieu of a constructor.
|
52
|
+
@lt2_mode ||= :lines
|
53
|
+
@lt2_delimiter ||= "\n"
|
54
|
+
@lt2_linebuffer ||= []
|
55
|
+
|
56
|
+
if @lt2_mode == :lines
|
57
|
+
if ix = data.index( @lt2_delimiter )
|
58
|
+
@lt2_linebuffer << data[0...ix]
|
59
|
+
ln = @lt2_linebuffer.join
|
60
|
+
@lt2_linebuffer.clear
|
61
|
+
if @lt2_delimiter == "\n"
|
62
|
+
ln.chomp!
|
63
|
+
end
|
64
|
+
receive_line ln
|
65
|
+
receive_data data[(ix+@lt2_delimiter.length)..-1]
|
66
|
+
else
|
67
|
+
@lt2_linebuffer << data
|
68
|
+
end
|
69
|
+
elsif @lt2_mode == :text
|
70
|
+
if @lt2_textsize
|
71
|
+
needed = @lt2_textsize - @lt2_textpos
|
72
|
+
will_take = if data.length > needed
|
73
|
+
needed
|
74
|
+
else
|
75
|
+
data.length
|
76
|
+
end
|
77
|
+
|
78
|
+
@lt2_textbuffer << data[0...will_take]
|
79
|
+
tail = data[will_take..-1]
|
80
|
+
|
81
|
+
@lt2_textpos += will_take
|
82
|
+
if @lt2_textpos >= @lt2_textsize
|
83
|
+
# Reset line mode (the default behavior) BEFORE calling the
|
84
|
+
# receive_binary_data. This makes it possible for user code
|
85
|
+
# to call set_text_mode, enabling chains of text blocks
|
86
|
+
# (which can possibly be of different sizes).
|
87
|
+
set_line_mode
|
88
|
+
receive_binary_data @lt2_textbuffer.join
|
89
|
+
receive_end_of_binary_data
|
90
|
+
end
|
91
|
+
|
92
|
+
receive_data tail
|
93
|
+
else
|
94
|
+
receive_binary_data data
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def set_delimiter delim
|
101
|
+
@lt2_delimiter = delim.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
# Called internally but also exposed to user code, for the case in which
|
105
|
+
# processing of binary data creates a need to transition back to line mode.
|
106
|
+
# We support an optional parameter to "throw back" some data, which might
|
107
|
+
# be an umprocessed chunk of the transmitted binary data, or something else
|
108
|
+
# entirely.
|
109
|
+
def set_line_mode data=""
|
110
|
+
@lt2_mode = :lines
|
111
|
+
(@lt2_linebuffer ||= []).clear
|
112
|
+
receive_data data.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_text_mode size=nil
|
116
|
+
if size == 0
|
117
|
+
set_line_mode
|
118
|
+
else
|
119
|
+
@lt2_mode = :text
|
120
|
+
(@lt2_textbuffer ||= []).clear
|
121
|
+
@lt2_textsize = size # which can be nil, signifying no limit
|
122
|
+
@lt2_textpos = 0
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
|
127
|
+
def set_binary_mode size=nil
|
128
|
+
set_text_mode size
|
129
|
+
end
|
130
|
+
|
131
|
+
# In case of a dropped connection, we'll send a partial buffer to user code
|
132
|
+
# when in sized text mode. User overrides of #receive_binary_data need to
|
133
|
+
# be aware that they may get a short buffer.
|
134
|
+
def unbind
|
135
|
+
if @lt2_mode == :text and @lt2_textpos > 0
|
136
|
+
receive_binary_data @lt2_textbuffer.join
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Stub. Should be subclassed by user code.
|
141
|
+
def receive_line ln
|
142
|
+
# no-op
|
143
|
+
end
|
144
|
+
|
145
|
+
# Stub. Should be subclassed by user code.
|
146
|
+
def receive_binary_data data
|
147
|
+
# no-op
|
148
|
+
end
|
149
|
+
|
150
|
+
# Stub. Should be subclassed by user code.
|
151
|
+
# This is called when transitioning internally from text mode
|
152
|
+
# back to line mode. Useful when client code doesn't want
|
153
|
+
# to keep track of how much data it's received.
|
154
|
+
def receive_end_of_binary_data
|
155
|
+
# no-op
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
@@ -32,6 +32,7 @@ module EventMachine
|
|
32
32
|
##
|
33
33
|
# constants
|
34
34
|
|
35
|
+
# :stopdoc:
|
35
36
|
unless defined? Cempty
|
36
37
|
Cstored = 'STORED'.freeze
|
37
38
|
Cend = 'END'.freeze
|
@@ -42,10 +43,16 @@ module EventMachine
|
|
42
43
|
Cempty = ''.freeze
|
43
44
|
Cdelimiter = "\r\n".freeze
|
44
45
|
end
|
46
|
+
# :startdoc:
|
45
47
|
|
46
48
|
##
|
47
49
|
# commands
|
48
50
|
|
51
|
+
# Get the value associated with one or multiple keys
|
52
|
+
#
|
53
|
+
# cache.get(:a){ |v| p v }
|
54
|
+
# cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
|
55
|
+
#
|
49
56
|
def get *keys
|
50
57
|
raise ArgumentError unless block_given?
|
51
58
|
|
@@ -58,6 +65,11 @@ module EventMachine
|
|
58
65
|
}
|
59
66
|
end
|
60
67
|
|
68
|
+
# Set the value for a given key
|
69
|
+
#
|
70
|
+
# cache.set :a, 'hello'
|
71
|
+
# cache.set(:missing, 'abc'){ puts "stored the value!" }
|
72
|
+
#
|
61
73
|
def set key, val, exptime = 0, &cb
|
62
74
|
callback{
|
63
75
|
val = val.to_s
|
@@ -68,6 +80,10 @@ module EventMachine
|
|
68
80
|
}
|
69
81
|
end
|
70
82
|
|
83
|
+
# Gets multiple values as a hash
|
84
|
+
#
|
85
|
+
# cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }
|
86
|
+
#
|
71
87
|
def get_hash *keys
|
72
88
|
raise ArgumentError unless block_given?
|
73
89
|
|
@@ -76,6 +92,11 @@ module EventMachine
|
|
76
92
|
end
|
77
93
|
end
|
78
94
|
|
95
|
+
# Delete the value associated with a key
|
96
|
+
#
|
97
|
+
# cache.del :a
|
98
|
+
# cache.del(:b){ puts "deleted the value!" }
|
99
|
+
#
|
79
100
|
def delete key, expires = 0, &cb
|
80
101
|
callback{
|
81
102
|
send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"
|
@@ -84,7 +105,14 @@ module EventMachine
|
|
84
105
|
end
|
85
106
|
alias del delete
|
86
107
|
|
87
|
-
|
108
|
+
# Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
|
109
|
+
def self.connect host = 'localhost', port = 11211
|
110
|
+
EM.connect host, port, self, host, port
|
111
|
+
end
|
112
|
+
|
113
|
+
# :stopdoc:
|
114
|
+
|
115
|
+
def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false # :nodoc:
|
88
116
|
send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
|
89
117
|
end
|
90
118
|
private :send_cmd
|
@@ -92,15 +120,12 @@ module EventMachine
|
|
92
120
|
##
|
93
121
|
# errors
|
94
122
|
|
95
|
-
class ParserError < StandardError
|
123
|
+
class ParserError < StandardError
|
124
|
+
end
|
96
125
|
|
97
126
|
##
|
98
127
|
# em hooks
|
99
128
|
|
100
|
-
def self.connect host = 'localhost', port = 11211
|
101
|
-
EM.connect host, port, self, host, port
|
102
|
-
end
|
103
|
-
|
104
129
|
def initialize host, port = 11211
|
105
130
|
@host, @port = host, port
|
106
131
|
end
|
@@ -119,6 +144,7 @@ module EventMachine
|
|
119
144
|
# set_line_mode
|
120
145
|
end
|
121
146
|
|
147
|
+
#--
|
122
148
|
# 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
|
123
149
|
# stack overflows when there is too much data.
|
124
150
|
# include EM::P::LineText2
|
@@ -136,6 +162,7 @@ module EventMachine
|
|
136
162
|
end
|
137
163
|
end
|
138
164
|
|
165
|
+
#--
|
139
166
|
# def receive_line line
|
140
167
|
def process_cmd line
|
141
168
|
case line.strip
|
@@ -177,6 +204,7 @@ module EventMachine
|
|
177
204
|
end
|
178
205
|
end
|
179
206
|
|
207
|
+
#--
|
180
208
|
# def receive_binary_data data
|
181
209
|
# @values[@cur_key] = data[0..-3]
|
182
210
|
# end
|
@@ -191,6 +219,8 @@ module EventMachine
|
|
191
219
|
raise 'Unable to connect to memcached server'
|
192
220
|
end
|
193
221
|
end
|
222
|
+
|
223
|
+
# :startdoc:
|
194
224
|
end
|
195
225
|
end
|
196
226
|
end
|
@@ -199,7 +229,7 @@ if __FILE__ == $0
|
|
199
229
|
# ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
|
200
230
|
require 'em/spec'
|
201
231
|
|
202
|
-
class TestConnection
|
232
|
+
class TestConnection # :nodoc:
|
203
233
|
include EM::P::Memcache
|
204
234
|
def send_data data
|
205
235
|
sent_data << data
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module EventMachine
|
2
|
+
module Protocols
|
3
|
+
# ObjectProtocol allows for easy communication using marshaled ruby objects
|
4
|
+
#
|
5
|
+
# module RubyServer
|
6
|
+
# include EM::P::ObjectProtocol
|
7
|
+
#
|
8
|
+
# def receive_object obj
|
9
|
+
# send_object({'you said' => obj})
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
module ObjectProtocol
|
14
|
+
def receive_data data # :nodoc:
|
15
|
+
(@buf ||= '') << data
|
16
|
+
|
17
|
+
while @buf.size >= 4
|
18
|
+
if @buf.size >= 4+(size=@buf.unpack('N').first)
|
19
|
+
@buf.slice!(0,4)
|
20
|
+
receive_object Marshal.load(@buf.slice!(0,size))
|
21
|
+
else
|
22
|
+
break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Invoked with ruby objects received over the network
|
28
|
+
def receive_object obj
|
29
|
+
# stub
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sends a ruby object over the network
|
33
|
+
def send_object obj
|
34
|
+
data = Marshal.dump(obj)
|
35
|
+
send_data [data.respond_to?(:bytesize) ? data.bytesize : data.size, data].pack('Na*')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
#--
|
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-08 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
|
+
require 'readbytes'
|
28
|
+
require 'postgres-pr/message'
|
29
|
+
require 'postgres-pr/connection'
|
30
|
+
require 'stringio'
|
31
|
+
|
32
|
+
class StringIO # :nodoc:
|
33
|
+
# Reads exactly +n+ bytes.
|
34
|
+
#
|
35
|
+
# If the data read is nil an EOFError is raised.
|
36
|
+
#
|
37
|
+
# If the data read is too short a TruncatedDataError is raised and the read
|
38
|
+
# data is obtainable via its #data method.
|
39
|
+
def readbytes(n)
|
40
|
+
str = read(n)
|
41
|
+
if str == nil
|
42
|
+
raise EOFError, "End of file reached"
|
43
|
+
end
|
44
|
+
if str.size < n
|
45
|
+
raise TruncatedDataError.new("data truncated", str)
|
46
|
+
end
|
47
|
+
str
|
48
|
+
end
|
49
|
+
alias read_exactly_n_bytes readbytes
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
module EventMachine
|
54
|
+
module Protocols
|
55
|
+
# PROVISIONAL IMPLEMENTATION of an evented Postgres client.
|
56
|
+
# This implements version 3 of the Postgres wire protocol, which will work
|
57
|
+
# with any Postgres version from roughly 7.4 onward.
|
58
|
+
#
|
59
|
+
# Objective: we want to access Postgres databases without requiring threads.
|
60
|
+
# Until now this has been a problem because the Postgres client implementations
|
61
|
+
# have all made use of blocking I/O calls, which is incompatible with a
|
62
|
+
# thread-free evented model.
|
63
|
+
#
|
64
|
+
# But rather than re-implement the Postgres Wire3 protocol, we're taking advantage
|
65
|
+
# of the existing postgres-pr library, which was originally written by Michael
|
66
|
+
# Neumann but (at this writing) appears to be no longer maintained. Still, it's
|
67
|
+
# in basically a production-ready state, and the wire protocol isn't that complicated
|
68
|
+
# anyway.
|
69
|
+
#
|
70
|
+
# We need to monkeypatch StringIO because it lacks the #readbytes method needed
|
71
|
+
# by postgres-pr.
|
72
|
+
#
|
73
|
+
# We're tucking in a bunch of require statements that may not be present in garden-variety
|
74
|
+
# EM installations. Until we find a good way to only require these if a program
|
75
|
+
# requires postgres, this file will need to be required explicitly.
|
76
|
+
#
|
77
|
+
# The StringIO monkeypatch is lifted verbatim from the standard library readbytes.rb,
|
78
|
+
# which adds method #readbytes directly to class IO. But StringIO is not a subclass of IO.
|
79
|
+
#
|
80
|
+
# We cloned the handling of postgres messages from lib/postgres-pr/connection.rb
|
81
|
+
# in the postgres-pr library, and modified it for event-handling.
|
82
|
+
#
|
83
|
+
# TODO: The password handling in dispatch_conn_message is totally incomplete.
|
84
|
+
#
|
85
|
+
#
|
86
|
+
# We return Deferrables from the user-level operations surfaced by this interface.
|
87
|
+
# Experimentally, we're using the pattern of always returning a boolean value as the
|
88
|
+
# first argument of a deferrable callback to indicate success or failure. This is
|
89
|
+
# instead of the traditional pattern of calling Deferrable#succeed or #fail, and
|
90
|
+
# requiring the user to define both a callback and an errback function.
|
91
|
+
#
|
92
|
+
# === Usage
|
93
|
+
# EM.run {
|
94
|
+
# db = EM.connect_unix_domain( "/tmp/.s.PGSQL.5432", EM::P::Postgres3 )
|
95
|
+
# db.connect( dbname, username, psw ).callback do |status|
|
96
|
+
# if status
|
97
|
+
# db.query( "select * from some_table" ).callback do |status, result, errors|
|
98
|
+
# if status
|
99
|
+
# result.rows.each do |row|
|
100
|
+
# p row
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
# }
|
107
|
+
class Postgres3 < EventMachine::Connection
|
108
|
+
include PostgresPR
|
109
|
+
|
110
|
+
def initialize
|
111
|
+
@data = ""
|
112
|
+
@params = {}
|
113
|
+
end
|
114
|
+
|
115
|
+
def connect db, user, psw=nil
|
116
|
+
d = EM::DefaultDeferrable.new
|
117
|
+
d.timeout 15
|
118
|
+
|
119
|
+
if @pending_query || @pending_conn
|
120
|
+
d.succeed false, "Operation already in progress"
|
121
|
+
else
|
122
|
+
@pending_conn = d
|
123
|
+
prms = {"user"=>user, "database"=>db}
|
124
|
+
@user = user
|
125
|
+
if psw
|
126
|
+
@password = psw
|
127
|
+
#prms["password"] = psw
|
128
|
+
end
|
129
|
+
send_data PostgresPR::StartupMessage.new( 3 << 16, prms ).dump
|
130
|
+
end
|
131
|
+
|
132
|
+
d
|
133
|
+
end
|
134
|
+
|
135
|
+
def query sql
|
136
|
+
d = EM::DefaultDeferrable.new
|
137
|
+
d.timeout 15
|
138
|
+
|
139
|
+
if @pending_query || @pending_conn
|
140
|
+
d.succeed false, "Operation already in progress"
|
141
|
+
else
|
142
|
+
@r = PostgresPR::Connection::Result.new
|
143
|
+
@e = []
|
144
|
+
@pending_query = d
|
145
|
+
send_data PostgresPR::Query.dump(sql)
|
146
|
+
end
|
147
|
+
|
148
|
+
d
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def receive_data data
|
153
|
+
@data << data
|
154
|
+
while @data.length >= 5
|
155
|
+
pktlen = @data[1...5].unpack("N").first
|
156
|
+
if @data.length >= (1 + pktlen)
|
157
|
+
pkt = @data.slice!(0...(1+pktlen))
|
158
|
+
m = StringIO.open( pkt, "r" ) {|io| PostgresPR::Message.read( io ) }
|
159
|
+
if @pending_conn
|
160
|
+
dispatch_conn_message m
|
161
|
+
elsif @pending_query
|
162
|
+
dispatch_query_message m
|
163
|
+
else
|
164
|
+
raise "Unexpected message from database"
|
165
|
+
end
|
166
|
+
else
|
167
|
+
break # very important, break out of the while
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def unbind
|
174
|
+
if o = (@pending_query || @pending_conn)
|
175
|
+
o.succeed false, "lost connection"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Cloned and modified from the postgres-pr.
|
180
|
+
def dispatch_conn_message msg
|
181
|
+
case msg
|
182
|
+
when AuthentificationClearTextPassword
|
183
|
+
raise ArgumentError, "no password specified" if @password.nil?
|
184
|
+
send_data PasswordMessage.new(@password).dump
|
185
|
+
|
186
|
+
when AuthentificationCryptPassword
|
187
|
+
raise ArgumentError, "no password specified" if @password.nil?
|
188
|
+
send_data PasswordMessage.new(@password.crypt(msg.salt)).dump
|
189
|
+
|
190
|
+
when AuthentificationMD5Password
|
191
|
+
raise ArgumentError, "no password specified" if @password.nil?
|
192
|
+
require 'digest/md5'
|
193
|
+
|
194
|
+
m = Digest::MD5.hexdigest(@password + @user)
|
195
|
+
m = Digest::MD5.hexdigest(m + msg.salt)
|
196
|
+
m = 'md5' + m
|
197
|
+
send_data PasswordMessage.new(m).dump
|
198
|
+
|
199
|
+
when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential
|
200
|
+
raise "unsupported authentification"
|
201
|
+
|
202
|
+
when AuthentificationOk
|
203
|
+
when ErrorResponse
|
204
|
+
raise msg.field_values.join("\t")
|
205
|
+
when NoticeResponse
|
206
|
+
@notice_processor.call(msg) if @notice_processor
|
207
|
+
when ParameterStatus
|
208
|
+
@params[msg.key] = msg.value
|
209
|
+
when BackendKeyData
|
210
|
+
# TODO
|
211
|
+
#p msg
|
212
|
+
when ReadyForQuery
|
213
|
+
# TODO: use transaction status
|
214
|
+
pc,@pending_conn = @pending_conn,nil
|
215
|
+
pc.succeed true
|
216
|
+
else
|
217
|
+
raise "unhandled message type"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Cloned and modified from the postgres-pr.
|
222
|
+
def dispatch_query_message msg
|
223
|
+
case msg
|
224
|
+
when DataRow
|
225
|
+
@r.rows << msg.columns
|
226
|
+
when CommandComplete
|
227
|
+
@r.cmd_tag = msg.cmd_tag
|
228
|
+
when ReadyForQuery
|
229
|
+
pq,@pending_query = @pending_query,nil
|
230
|
+
pq.succeed true, @r, @e
|
231
|
+
when RowDescription
|
232
|
+
@r.fields = msg.fields
|
233
|
+
when CopyInResponse
|
234
|
+
when CopyOutResponse
|
235
|
+
when EmptyQueryResponse
|
236
|
+
when ErrorResponse
|
237
|
+
# TODO
|
238
|
+
@e << msg
|
239
|
+
when NoticeResponse
|
240
|
+
@notice_processor.call(msg) if @notice_processor
|
241
|
+
else
|
242
|
+
# TODO
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|