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
data/lib/protocols/httpclient.rb
DELETED
@@ -1,270 +0,0 @@
|
|
1
|
-
# $Id$
|
2
|
-
#
|
3
|
-
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
-
# Homepage:: http://rubyeventmachine.com
|
5
|
-
# Date:: 16 July 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
|
-
|
27
|
-
|
28
|
-
module EventMachine
|
29
|
-
module Protocols
|
30
|
-
|
31
|
-
class HttpClient < Connection
|
32
|
-
include EventMachine::Deferrable
|
33
|
-
|
34
|
-
|
35
|
-
MaxPostContentLength = 20 * 1024 * 1024
|
36
|
-
|
37
|
-
# USAGE SAMPLE:
|
38
|
-
#
|
39
|
-
# EventMachine.run {
|
40
|
-
# http = EventMachine::Protocols::HttpClient.request(
|
41
|
-
# :host => server,
|
42
|
-
# :port => 80,
|
43
|
-
# :request => "/index.html",
|
44
|
-
# :query_string => "parm1=value1&parm2=value2"
|
45
|
-
# )
|
46
|
-
# http.callback {|response|
|
47
|
-
# puts response[:status]
|
48
|
-
# puts response[:headers]
|
49
|
-
# puts response[:content]
|
50
|
-
# }
|
51
|
-
# }
|
52
|
-
#
|
53
|
-
|
54
|
-
# TODO:
|
55
|
-
# Add streaming so we can support enormous POSTs. Current max is 20meg.
|
56
|
-
# Timeout for connections that run too long or hang somewhere in the middle.
|
57
|
-
# Persistent connections (HTTP/1.1), may need a associated delegate object.
|
58
|
-
# DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
|
59
|
-
# DNS lookups are unbelievably slow.
|
60
|
-
# HEAD requests.
|
61
|
-
# Chunked transfer encoding.
|
62
|
-
# Convenience methods for requests. get, post, url, etc.
|
63
|
-
# SSL.
|
64
|
-
# Handle status codes like 304, 100, etc.
|
65
|
-
# Refactor this code so that protocol errors all get handled one way (an exception?),
|
66
|
-
# instead of sprinkling set_deferred_status :failed calls everywhere.
|
67
|
-
|
68
|
-
# === Arg list
|
69
|
-
# :host => 'ip/dns', :port => fixnum, :verb => 'GET', :request => 'path',
|
70
|
-
# :basic_auth => {:username => '', :password => ''}, :content => 'content',
|
71
|
-
# :contenttype => 'text/plain', :query_string => '', :host_header => '',
|
72
|
-
# :cookie => ''
|
73
|
-
|
74
|
-
def self.request( args = {} )
|
75
|
-
args[:port] ||= 80
|
76
|
-
EventMachine.connect( args[:host], args[:port], self ) {|c|
|
77
|
-
# According to the docs, we will get here AFTER post_init is called.
|
78
|
-
c.instance_eval {@args = args}
|
79
|
-
}
|
80
|
-
end
|
81
|
-
|
82
|
-
def post_init
|
83
|
-
@start_time = Time.now
|
84
|
-
@data = ""
|
85
|
-
@read_state = :base
|
86
|
-
end
|
87
|
-
|
88
|
-
# We send the request when we get a connection.
|
89
|
-
# AND, we set an instance variable to indicate we passed through here.
|
90
|
-
# That allows #unbind to know whether there was a successful connection.
|
91
|
-
# NB: This naive technique won't work when we have to support multiple
|
92
|
-
# requests on a single connection.
|
93
|
-
def connection_completed
|
94
|
-
@connected = true
|
95
|
-
send_request @args
|
96
|
-
end
|
97
|
-
|
98
|
-
def send_request args
|
99
|
-
args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
|
100
|
-
args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
|
101
|
-
|
102
|
-
verb = args[:verb].to_s.upcase
|
103
|
-
unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
|
104
|
-
set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
|
105
|
-
return # NOTE THE EARLY RETURN, we're not sending any data.
|
106
|
-
end
|
107
|
-
|
108
|
-
request = args[:request] || "/"
|
109
|
-
unless request[0,1] == "/"
|
110
|
-
request = "/" + request
|
111
|
-
end
|
112
|
-
|
113
|
-
qs = args[:query_string] || ""
|
114
|
-
if qs.length > 0 and qs[0,1] != '?'
|
115
|
-
qs = "?" + qs
|
116
|
-
end
|
117
|
-
|
118
|
-
version = args[:version] || "1.1"
|
119
|
-
|
120
|
-
# Allow an override for the host header if it's not the connect-string.
|
121
|
-
host = args[:host_header] || args[:host] || "_"
|
122
|
-
# For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
|
123
|
-
port = args[:port]
|
124
|
-
|
125
|
-
# POST items.
|
126
|
-
postcontenttype = args[:contenttype] || "application/octet-stream"
|
127
|
-
postcontent = args[:content] || ""
|
128
|
-
raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
|
129
|
-
|
130
|
-
# ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
|
131
|
-
# TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
|
132
|
-
req = [
|
133
|
-
"#{verb} #{request}#{qs} HTTP/#{version}",
|
134
|
-
"Host: #{host}:#{port}",
|
135
|
-
"User-agent: Ruby EventMachine",
|
136
|
-
]
|
137
|
-
|
138
|
-
if verb == "POST" || verb == "PUT"
|
139
|
-
req << "Content-type: #{postcontenttype}"
|
140
|
-
req << "Content-length: #{postcontent.length}"
|
141
|
-
end
|
142
|
-
|
143
|
-
# TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
|
144
|
-
# Eventually we will want to deal intelligently with arrays and hashes.
|
145
|
-
if args[:cookie]
|
146
|
-
req << "Cookie: #{args[:cookie]}"
|
147
|
-
end
|
148
|
-
|
149
|
-
# Basic-auth stanza contributed by Matt Murphy.
|
150
|
-
if args[:basic_auth]
|
151
|
-
basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
|
152
|
-
req << "Authorization: Basic #{basic_auth_string}"
|
153
|
-
end
|
154
|
-
|
155
|
-
req << ""
|
156
|
-
reqstring = req.map {|l| "#{l}\r\n"}.join
|
157
|
-
send_data reqstring
|
158
|
-
|
159
|
-
if verb == "POST" || verb == "PUT"
|
160
|
-
send_data postcontent
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
|
165
|
-
def receive_data data
|
166
|
-
while data and data.length > 0
|
167
|
-
case @read_state
|
168
|
-
when :base
|
169
|
-
# Perform any per-request initialization here and don't consume any data.
|
170
|
-
@data = ""
|
171
|
-
@headers = []
|
172
|
-
@content_length = nil # not zero
|
173
|
-
@content = ""
|
174
|
-
@status = nil
|
175
|
-
@read_state = :header
|
176
|
-
@connection_close = nil
|
177
|
-
when :header
|
178
|
-
ary = data.split( /\r?\n/m, 2 )
|
179
|
-
if ary.length == 2
|
180
|
-
data = ary.last
|
181
|
-
if ary.first == ""
|
182
|
-
if (@content_length and @content_length > 0) || @connection_close
|
183
|
-
@read_state = :content
|
184
|
-
else
|
185
|
-
dispatch_response
|
186
|
-
@read_state = :base
|
187
|
-
end
|
188
|
-
else
|
189
|
-
@headers << ary.first
|
190
|
-
if @headers.length == 1
|
191
|
-
parse_response_line
|
192
|
-
elsif ary.first =~ /\Acontent-length:\s*/i
|
193
|
-
# Only take the FIRST content-length header that appears,
|
194
|
-
# which we can distinguish because @content_length is nil.
|
195
|
-
# TODO, it's actually a fatal error if there is more than one
|
196
|
-
# content-length header, because the caller is presumptively
|
197
|
-
# a bad guy. (There is an exploit that depends on multiple
|
198
|
-
# content-length headers.)
|
199
|
-
@content_length ||= $'.to_i
|
200
|
-
elsif ary.first =~ /\Aconnection:\s*close/i
|
201
|
-
@connection_close = true
|
202
|
-
end
|
203
|
-
end
|
204
|
-
else
|
205
|
-
@data << data
|
206
|
-
data = ""
|
207
|
-
end
|
208
|
-
when :content
|
209
|
-
# If there was no content-length header, we have to wait until the connection
|
210
|
-
# closes. Everything we get until that point is content.
|
211
|
-
# TODO: Must impose a content-size limit, and also must implement chunking.
|
212
|
-
# Also, must support either temporary files for large content, or calling
|
213
|
-
# a content-consumer block supplied by the user.
|
214
|
-
if @content_length
|
215
|
-
bytes_needed = @content_length - @content.length
|
216
|
-
@content += data[0, bytes_needed]
|
217
|
-
data = data[bytes_needed..-1] || ""
|
218
|
-
if @content_length == @content.length
|
219
|
-
dispatch_response
|
220
|
-
@read_state = :base
|
221
|
-
end
|
222
|
-
else
|
223
|
-
@content << data
|
224
|
-
data = ""
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
|
231
|
-
# We get called here when we have received an HTTP response line.
|
232
|
-
# It's an opportunity to throw an exception or trigger other exceptional
|
233
|
-
# handling.
|
234
|
-
def parse_response_line
|
235
|
-
if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
|
236
|
-
@status = $1.to_i
|
237
|
-
else
|
238
|
-
set_deferred_status :failed, {
|
239
|
-
:status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
|
240
|
-
}
|
241
|
-
close_connection
|
242
|
-
end
|
243
|
-
end
|
244
|
-
private :parse_response_line
|
245
|
-
|
246
|
-
def dispatch_response
|
247
|
-
@read_state = :base
|
248
|
-
set_deferred_status :succeeded, {
|
249
|
-
:content => @content,
|
250
|
-
:headers => @headers,
|
251
|
-
:status => @status
|
252
|
-
}
|
253
|
-
# TODO, we close the connection for now, but this is wrong for persistent clients.
|
254
|
-
close_connection
|
255
|
-
end
|
256
|
-
|
257
|
-
def unbind
|
258
|
-
if !@connected
|
259
|
-
set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
|
260
|
-
elsif (@read_state == :content and @content_length == nil)
|
261
|
-
dispatch_response
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
|
data/lib/protocols/linetext2.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
# $Id$
|
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
|
-
|
27
|
-
module EventMachine
|
28
|
-
module Protocols
|
29
|
-
# In the grand, time-honored tradition of re-inventing the wheel, we offer
|
30
|
-
# here YET ANOTHER protocol that handles line-oriented data with interspersed
|
31
|
-
# binary text. This one trades away some of the performance optimizations of
|
32
|
-
# EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
|
33
|
-
# with regard to binary text blocks that can switch back to line mode. It also
|
34
|
-
# permits the line-delimiter to change in midstream.
|
35
|
-
# This was originally written to support Stomp.
|
36
|
-
module LineText2
|
37
|
-
# TODO! We're not enforcing the limits on header lengths and text-lengths.
|
38
|
-
# When we get around to that, call #receive_error if the user defined it, otherwise
|
39
|
-
# throw exceptions.
|
40
|
-
|
41
|
-
MaxLineLength = 16*1024
|
42
|
-
MaxBinaryLength = 32*1024*1024
|
43
|
-
|
44
|
-
#--
|
45
|
-
# Will be called recursively until there's no data to read.
|
46
|
-
# That way the user-defined handlers we call can modify the
|
47
|
-
# handling characteristics on a per-token basis.
|
48
|
-
#
|
49
|
-
def receive_data data
|
50
|
-
return unless (data and data.length > 0)
|
51
|
-
|
52
|
-
# Do this stuff in lieu of a constructor.
|
53
|
-
@lt2_mode ||= :lines
|
54
|
-
@lt2_delimiter ||= "\n"
|
55
|
-
@lt2_linebuffer ||= []
|
56
|
-
|
57
|
-
if @lt2_mode == :lines
|
58
|
-
if ix = data.index( @lt2_delimiter )
|
59
|
-
@lt2_linebuffer << data[0...ix]
|
60
|
-
ln = @lt2_linebuffer.join
|
61
|
-
@lt2_linebuffer.clear
|
62
|
-
if @lt2_delimiter == "\n"
|
63
|
-
ln.chomp!
|
64
|
-
end
|
65
|
-
receive_line ln
|
66
|
-
receive_data data[(ix+@lt2_delimiter.length)..-1]
|
67
|
-
else
|
68
|
-
@lt2_linebuffer << data
|
69
|
-
end
|
70
|
-
elsif @lt2_mode == :text
|
71
|
-
if @lt2_textsize
|
72
|
-
needed = @lt2_textsize - @lt2_textpos
|
73
|
-
will_take = if data.length > needed
|
74
|
-
needed
|
75
|
-
else
|
76
|
-
data.length
|
77
|
-
end
|
78
|
-
|
79
|
-
@lt2_textbuffer << data[0...will_take]
|
80
|
-
tail = data[will_take..-1]
|
81
|
-
|
82
|
-
@lt2_textpos += will_take
|
83
|
-
if @lt2_textpos >= @lt2_textsize
|
84
|
-
# Reset line mode (the default behavior) BEFORE calling the
|
85
|
-
# receive_binary_data. This makes it possible for user code
|
86
|
-
# to call set_text_mode, enabling chains of text blocks
|
87
|
-
# (which can possibly be of different sizes).
|
88
|
-
set_line_mode
|
89
|
-
receive_binary_data @lt2_textbuffer.join
|
90
|
-
receive_end_of_binary_data
|
91
|
-
end
|
92
|
-
|
93
|
-
receive_data tail
|
94
|
-
else
|
95
|
-
receive_binary_data data
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
def set_delimiter delim
|
102
|
-
@lt2_delimiter = delim.to_s
|
103
|
-
end
|
104
|
-
|
105
|
-
# Called internally but also exposed to user code, for the case in which
|
106
|
-
# processing of binary data creates a need to transition back to line mode.
|
107
|
-
# We support an optional parameter to "throw back" some data, which might
|
108
|
-
# be an umprocessed chunk of the transmitted binary data, or something else
|
109
|
-
# entirely.
|
110
|
-
def set_line_mode data=""
|
111
|
-
@lt2_mode = :lines
|
112
|
-
(@lt2_linebuffer ||= []).clear
|
113
|
-
receive_data data.to_s
|
114
|
-
end
|
115
|
-
|
116
|
-
def set_text_mode size=nil
|
117
|
-
if size == 0
|
118
|
-
set_line_mode
|
119
|
-
else
|
120
|
-
@lt2_mode = :text
|
121
|
-
(@lt2_textbuffer ||= []).clear
|
122
|
-
@lt2_textsize = size # which can be nil, signifying no limit
|
123
|
-
@lt2_textpos = 0
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
|
128
|
-
def set_binary_mode size=nil
|
129
|
-
set_text_mode size
|
130
|
-
end
|
131
|
-
|
132
|
-
# In case of a dropped connection, we'll send a partial buffer to user code
|
133
|
-
# when in sized text mode. User overrides of #receive_binary_data need to
|
134
|
-
# be aware that they may get a short buffer.
|
135
|
-
def unbind
|
136
|
-
if @lt2_mode == :text and @lt2_textpos > 0
|
137
|
-
receive_binary_data @lt2_textbuffer.join
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Stub. Should be subclassed by user code.
|
142
|
-
def receive_line ln
|
143
|
-
# no-op
|
144
|
-
end
|
145
|
-
|
146
|
-
# Stub. Should be subclassed by user code.
|
147
|
-
def receive_binary_data data
|
148
|
-
# no-op
|
149
|
-
end
|
150
|
-
|
151
|
-
# Stub. Should be subclassed by user code.
|
152
|
-
# This is called when transitioning internally from text mode
|
153
|
-
# back to line mode. Useful when client code doesn't want
|
154
|
-
# to keep track of how much data it's received.
|
155
|
-
def receive_end_of_binary_data
|
156
|
-
# no-op
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
data/lib/protocols/postgres.rb
DELETED
@@ -1,261 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# $Id$
|
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
|
-
|