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.
Files changed (116) hide show
  1. data/{docs/README → README} +21 -13
  2. data/Rakefile +14 -4
  3. data/docs/DEFERRABLES +0 -5
  4. data/docs/INSTALL +2 -4
  5. data/docs/LEGAL +1 -1
  6. data/docs/LIGHTWEIGHT_CONCURRENCY +0 -2
  7. data/docs/PURE_RUBY +0 -2
  8. data/docs/RELEASE_NOTES +0 -2
  9. data/docs/SMTP +0 -7
  10. data/docs/SPAWNED_PROCESSES +0 -4
  11. data/docs/TODO +0 -2
  12. data/eventmachine.gemspec +41 -32
  13. data/examples/ex_channel.rb +43 -0
  14. data/examples/ex_queue.rb +2 -0
  15. data/examples/helper.rb +2 -0
  16. data/ext/cmain.cpp +685 -586
  17. data/ext/cplusplus.cpp +15 -6
  18. data/ext/ed.cpp +1732 -1522
  19. data/ext/ed.h +407 -380
  20. data/ext/em.cpp +2263 -1937
  21. data/ext/em.h +223 -186
  22. data/ext/eventmachine.h +111 -98
  23. data/ext/eventmachine_cpp.h +1 -0
  24. data/ext/extconf.rb +4 -0
  25. data/ext/kb.cpp +81 -82
  26. data/ext/pipe.cpp +349 -351
  27. data/ext/project.h +21 -0
  28. data/ext/rubymain.cpp +1047 -847
  29. data/ext/ssl.cpp +38 -1
  30. data/ext/ssl.h +5 -1
  31. data/java/src/com/rubyeventmachine/Application.java +7 -3
  32. data/java/src/com/rubyeventmachine/EmReactor.java +16 -1
  33. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +25 -3
  34. data/lib/{protocols → em}/buftok.rb +16 -5
  35. data/lib/em/callback.rb +26 -0
  36. data/lib/em/channel.rb +57 -0
  37. data/lib/em/connection.rb +505 -0
  38. data/lib/em/deferrable.rb +144 -165
  39. data/lib/em/file_watch.rb +54 -0
  40. data/lib/em/future.rb +24 -25
  41. data/lib/em/messages.rb +1 -1
  42. data/lib/em/process_watch.rb +44 -0
  43. data/lib/em/processes.rb +119 -113
  44. data/lib/em/protocols.rb +35 -0
  45. data/lib/em/protocols/header_and_content.rb +138 -0
  46. data/lib/em/protocols/httpclient.rb +263 -0
  47. data/lib/em/protocols/httpclient2.rb +582 -0
  48. data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
  49. data/lib/em/protocols/linetext2.rb +160 -0
  50. data/lib/{protocols → em/protocols}/memcache.rb +37 -7
  51. data/lib/em/protocols/object_protocol.rb +39 -0
  52. data/lib/em/protocols/postgres3.rb +247 -0
  53. data/lib/em/protocols/saslauth.rb +175 -0
  54. data/lib/em/protocols/smtpclient.rb +331 -0
  55. data/lib/em/protocols/smtpserver.rb +547 -0
  56. data/lib/em/protocols/stomp.rb +200 -0
  57. data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
  58. data/lib/em/queue.rb +61 -0
  59. data/lib/em/spawnable.rb +53 -56
  60. data/lib/em/streamer.rb +92 -74
  61. data/lib/em/timers.rb +55 -0
  62. data/lib/em/version.rb +3 -0
  63. data/lib/eventmachine.rb +1636 -1926
  64. data/lib/evma.rb +1 -1
  65. data/lib/jeventmachine.rb +106 -101
  66. data/lib/pr_eventmachine.rb +47 -36
  67. data/tasks/project.rake +2 -1
  68. data/tests/client.crt +31 -0
  69. data/tests/client.key +51 -0
  70. data/tests/test_attach.rb +18 -0
  71. data/tests/test_basic.rb +285 -231
  72. data/tests/test_channel.rb +63 -0
  73. data/tests/test_connection_count.rb +2 -2
  74. data/tests/test_epoll.rb +162 -163
  75. data/tests/test_errors.rb +36 -36
  76. data/tests/test_exc.rb +22 -25
  77. data/tests/test_file_watch.rb +49 -0
  78. data/tests/test_futures.rb +77 -93
  79. data/tests/test_hc.rb +2 -2
  80. data/tests/test_httpclient.rb +55 -52
  81. data/tests/test_httpclient2.rb +153 -155
  82. data/tests/test_inactivity_timeout.rb +30 -0
  83. data/tests/test_kb.rb +8 -9
  84. data/tests/test_ltp2.rb +274 -277
  85. data/tests/test_next_tick.rb +135 -109
  86. data/tests/test_object_protocol.rb +37 -0
  87. data/tests/test_process_watch.rb +48 -0
  88. data/tests/test_processes.rb +128 -95
  89. data/tests/test_proxy_connection.rb +92 -0
  90. data/tests/test_pure.rb +1 -5
  91. data/tests/test_queue.rb +44 -0
  92. data/tests/test_running.rb +9 -14
  93. data/tests/test_sasl.rb +32 -34
  94. data/tests/test_send_file.rb +175 -176
  95. data/tests/test_servers.rb +37 -41
  96. data/tests/test_smtpserver.rb +47 -55
  97. data/tests/test_spawn.rb +284 -291
  98. data/tests/test_ssl_args.rb +1 -1
  99. data/tests/test_ssl_methods.rb +1 -1
  100. data/tests/test_ssl_verify.rb +82 -0
  101. data/tests/test_timers.rb +81 -88
  102. data/tests/test_ud.rb +0 -7
  103. data/tests/testem.rb +1 -1
  104. metadata +52 -36
  105. data/lib/em/eventable.rb +0 -39
  106. data/lib/eventmachine_version.rb +0 -31
  107. data/lib/protocols/header_and_content.rb +0 -129
  108. data/lib/protocols/httpcli2.rb +0 -803
  109. data/lib/protocols/httpclient.rb +0 -270
  110. data/lib/protocols/linetext2.rb +0 -161
  111. data/lib/protocols/postgres.rb +0 -261
  112. data/lib/protocols/saslauth.rb +0 -179
  113. data/lib/protocols/smtpclient.rb +0 -308
  114. data/lib/protocols/smtpserver.rb +0 -556
  115. data/lib/protocols/stomp.rb +0 -153
  116. data/tests/test_eventables.rb +0 -77
@@ -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
-
@@ -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
-
@@ -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
-