eventmachine 0.12.6 → 0.12.8

Sign up to get free protection for your applications and to get access to all the features.
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 +17 -8
  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 +119 -20
  17. data/ext/cplusplus.cpp +15 -6
  18. data/ext/ed.cpp +303 -93
  19. data/ext/ed.h +49 -22
  20. data/ext/em.cpp +368 -42
  21. data/ext/em.h +43 -6
  22. data/ext/eventmachine.h +21 -8
  23. data/ext/eventmachine_cpp.h +1 -0
  24. data/ext/extconf.rb +4 -0
  25. data/ext/kb.cpp +1 -2
  26. data/ext/pipe.cpp +1 -3
  27. data/ext/project.h +21 -0
  28. data/ext/rubymain.cpp +232 -32
  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 +58 -52
  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 +1008 -1298
  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 +108 -54
  72. data/tests/test_channel.rb +63 -0
  73. data/tests/test_connection_count.rb +2 -2
  74. data/tests/test_epoll.rb +109 -110
  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 +110 -112
  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 +91 -65
  86. data/tests/test_object_protocol.rb +37 -0
  87. data/tests/test_process_watch.rb +48 -0
  88. data/tests/test_processes.rb +56 -23
  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 +68 -39
  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
-