eventmachine 0.12.6-x86-mswin32-60 → 0.12.8-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|