eventmachine 1.2.0.dev.2-x64-mingw32

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +105 -0
  3. data/GNU +281 -0
  4. data/LICENSE +60 -0
  5. data/README.md +108 -0
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +521 -0
  8. data/docs/old/ChangeLog +211 -0
  9. data/docs/old/DEFERRABLES +246 -0
  10. data/docs/old/EPOLL +141 -0
  11. data/docs/old/INSTALL +13 -0
  12. data/docs/old/KEYBOARD +42 -0
  13. data/docs/old/LEGAL +25 -0
  14. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  15. data/docs/old/PURE_RUBY +75 -0
  16. data/docs/old/RELEASE_NOTES +94 -0
  17. data/docs/old/SMTP +4 -0
  18. data/docs/old/SPAWNED_PROCESSES +148 -0
  19. data/docs/old/TODO +8 -0
  20. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  21. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  22. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  23. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  24. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  25. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  26. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  27. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  28. data/examples/old/ex_channel.rb +43 -0
  29. data/examples/old/ex_queue.rb +2 -0
  30. data/examples/old/ex_tick_loop_array.rb +15 -0
  31. data/examples/old/ex_tick_loop_counter.rb +32 -0
  32. data/examples/old/helper.rb +2 -0
  33. data/ext/binder.cpp +124 -0
  34. data/ext/binder.h +46 -0
  35. data/ext/cmain.cpp +988 -0
  36. data/ext/ed.cpp +2111 -0
  37. data/ext/ed.h +442 -0
  38. data/ext/em.cpp +2379 -0
  39. data/ext/em.h +308 -0
  40. data/ext/eventmachine.h +143 -0
  41. data/ext/extconf.rb +270 -0
  42. data/ext/fastfilereader/extconf.rb +110 -0
  43. data/ext/fastfilereader/mapper.cpp +216 -0
  44. data/ext/fastfilereader/mapper.h +59 -0
  45. data/ext/fastfilereader/rubymain.cpp +127 -0
  46. data/ext/kb.cpp +79 -0
  47. data/ext/page.cpp +107 -0
  48. data/ext/page.h +51 -0
  49. data/ext/pipe.cpp +354 -0
  50. data/ext/project.h +176 -0
  51. data/ext/rubymain.cpp +1504 -0
  52. data/ext/ssl.cpp +615 -0
  53. data/ext/ssl.h +103 -0
  54. data/java/.classpath +8 -0
  55. data/java/.project +17 -0
  56. data/java/src/com/rubyeventmachine/EmReactor.java +591 -0
  57. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  58. data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
  59. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
  60. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
  61. data/lib/2.0/fastfilereaderext.so +0 -0
  62. data/lib/2.0/rubyeventmachine.so +0 -0
  63. data/lib/2.1/fastfilereaderext.so +0 -0
  64. data/lib/2.1/rubyeventmachine.so +0 -0
  65. data/lib/2.2/fastfilereaderext.so +0 -0
  66. data/lib/2.2/rubyeventmachine.so +0 -0
  67. data/lib/2.3/fastfilereaderext.so +0 -0
  68. data/lib/2.3/rubyeventmachine.so +0 -0
  69. data/lib/em/buftok.rb +59 -0
  70. data/lib/em/callback.rb +58 -0
  71. data/lib/em/channel.rb +69 -0
  72. data/lib/em/completion.rb +304 -0
  73. data/lib/em/connection.rb +770 -0
  74. data/lib/em/deferrable.rb +210 -0
  75. data/lib/em/deferrable/pool.rb +2 -0
  76. data/lib/em/file_watch.rb +73 -0
  77. data/lib/em/future.rb +61 -0
  78. data/lib/em/iterator.rb +252 -0
  79. data/lib/em/messages.rb +66 -0
  80. data/lib/em/pool.rb +151 -0
  81. data/lib/em/process_watch.rb +45 -0
  82. data/lib/em/processes.rb +123 -0
  83. data/lib/em/protocols.rb +37 -0
  84. data/lib/em/protocols/header_and_content.rb +138 -0
  85. data/lib/em/protocols/httpclient.rb +299 -0
  86. data/lib/em/protocols/httpclient2.rb +600 -0
  87. data/lib/em/protocols/line_and_text.rb +125 -0
  88. data/lib/em/protocols/line_protocol.rb +29 -0
  89. data/lib/em/protocols/linetext2.rb +166 -0
  90. data/lib/em/protocols/memcache.rb +331 -0
  91. data/lib/em/protocols/object_protocol.rb +46 -0
  92. data/lib/em/protocols/postgres3.rb +246 -0
  93. data/lib/em/protocols/saslauth.rb +175 -0
  94. data/lib/em/protocols/smtpclient.rb +394 -0
  95. data/lib/em/protocols/smtpserver.rb +666 -0
  96. data/lib/em/protocols/socks4.rb +66 -0
  97. data/lib/em/protocols/stomp.rb +205 -0
  98. data/lib/em/protocols/tcptest.rb +54 -0
  99. data/lib/em/pure_ruby.rb +1022 -0
  100. data/lib/em/queue.rb +80 -0
  101. data/lib/em/resolver.rb +232 -0
  102. data/lib/em/spawnable.rb +84 -0
  103. data/lib/em/streamer.rb +118 -0
  104. data/lib/em/threaded_resource.rb +90 -0
  105. data/lib/em/tick_loop.rb +85 -0
  106. data/lib/em/timers.rb +61 -0
  107. data/lib/em/version.rb +3 -0
  108. data/lib/eventmachine.rb +1584 -0
  109. data/lib/fastfilereaderext.rb +2 -0
  110. data/lib/jeventmachine.rb +301 -0
  111. data/lib/rubyeventmachine.rb +2 -0
  112. data/rakelib/package.rake +120 -0
  113. data/rakelib/test.rake +8 -0
  114. data/tests/client.crt +31 -0
  115. data/tests/client.key +51 -0
  116. data/tests/dhparam.pem +13 -0
  117. data/tests/em_test_helper.rb +151 -0
  118. data/tests/test_attach.rb +151 -0
  119. data/tests/test_basic.rb +283 -0
  120. data/tests/test_channel.rb +75 -0
  121. data/tests/test_completion.rb +178 -0
  122. data/tests/test_connection_count.rb +54 -0
  123. data/tests/test_connection_write.rb +35 -0
  124. data/tests/test_defer.rb +35 -0
  125. data/tests/test_deferrable.rb +35 -0
  126. data/tests/test_epoll.rb +142 -0
  127. data/tests/test_error_handler.rb +38 -0
  128. data/tests/test_exc.rb +28 -0
  129. data/tests/test_file_watch.rb +66 -0
  130. data/tests/test_fork.rb +75 -0
  131. data/tests/test_futures.rb +170 -0
  132. data/tests/test_get_sock_opt.rb +37 -0
  133. data/tests/test_handler_check.rb +35 -0
  134. data/tests/test_hc.rb +155 -0
  135. data/tests/test_httpclient.rb +233 -0
  136. data/tests/test_httpclient2.rb +128 -0
  137. data/tests/test_idle_connection.rb +25 -0
  138. data/tests/test_inactivity_timeout.rb +54 -0
  139. data/tests/test_ipv4.rb +125 -0
  140. data/tests/test_ipv6.rb +131 -0
  141. data/tests/test_iterator.rb +115 -0
  142. data/tests/test_kb.rb +28 -0
  143. data/tests/test_line_protocol.rb +33 -0
  144. data/tests/test_ltp.rb +138 -0
  145. data/tests/test_ltp2.rb +308 -0
  146. data/tests/test_many_fds.rb +22 -0
  147. data/tests/test_next_tick.rb +104 -0
  148. data/tests/test_object_protocol.rb +36 -0
  149. data/tests/test_pause.rb +107 -0
  150. data/tests/test_pending_connect_timeout.rb +52 -0
  151. data/tests/test_pool.rb +196 -0
  152. data/tests/test_process_watch.rb +50 -0
  153. data/tests/test_processes.rb +128 -0
  154. data/tests/test_proxy_connection.rb +180 -0
  155. data/tests/test_pure.rb +88 -0
  156. data/tests/test_queue.rb +64 -0
  157. data/tests/test_resolver.rb +104 -0
  158. data/tests/test_running.rb +14 -0
  159. data/tests/test_sasl.rb +47 -0
  160. data/tests/test_send_file.rb +217 -0
  161. data/tests/test_servers.rb +33 -0
  162. data/tests/test_set_sock_opt.rb +39 -0
  163. data/tests/test_shutdown_hooks.rb +23 -0
  164. data/tests/test_smtpclient.rb +75 -0
  165. data/tests/test_smtpserver.rb +57 -0
  166. data/tests/test_spawn.rb +293 -0
  167. data/tests/test_ssl_args.rb +78 -0
  168. data/tests/test_ssl_dhparam.rb +83 -0
  169. data/tests/test_ssl_ecdh_curve.rb +79 -0
  170. data/tests/test_ssl_extensions.rb +49 -0
  171. data/tests/test_ssl_methods.rb +65 -0
  172. data/tests/test_ssl_protocols.rb +246 -0
  173. data/tests/test_ssl_verify.rb +126 -0
  174. data/tests/test_stomp.rb +37 -0
  175. data/tests/test_system.rb +46 -0
  176. data/tests/test_threaded_resource.rb +61 -0
  177. data/tests/test_tick_loop.rb +59 -0
  178. data/tests/test_timers.rb +123 -0
  179. data/tests/test_ud.rb +8 -0
  180. data/tests/test_unbind_reason.rb +52 -0
  181. metadata +381 -0
@@ -0,0 +1,125 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+
27
+ module EventMachine
28
+ module Protocols
29
+ # A protocol that handles line-oriented data with interspersed binary text.
30
+ #
31
+ # This version is optimized for performance. See EventMachine::Protocols::LineText2
32
+ # for a version which is optimized for correctness with regard to binary text blocks
33
+ # that can switch back to line mode.
34
+ class LineAndTextProtocol < Connection
35
+ MaxBinaryLength = 32*1024*1024
36
+
37
+ def initialize *args
38
+ super
39
+ lbp_init_line_state
40
+ end
41
+
42
+ def receive_data data
43
+ if @lbp_mode == :lines
44
+ begin
45
+ @lpb_buffer.extract(data).each do |line|
46
+ receive_line(line.chomp) if respond_to?(:receive_line)
47
+ end
48
+ rescue
49
+ receive_error('overlength line') if respond_to?(:receive_error)
50
+ close_connection
51
+ return
52
+ end
53
+ else
54
+ if @lbp_binary_limit > 0
55
+ wanted = @lbp_binary_limit - @lbp_binary_bytes_received
56
+ chunk = nil
57
+ if data.length > wanted
58
+ chunk = data.slice!(0...wanted)
59
+ else
60
+ chunk = data
61
+ data = ""
62
+ end
63
+ @lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
64
+ @lbp_binary_bytes_received += chunk.length
65
+ if @lbp_binary_bytes_received == @lbp_binary_limit
66
+ receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
67
+ lbp_init_line_state
68
+ end
69
+ receive_data(data) if data.length > 0
70
+ else
71
+ receive_binary_data(data) if respond_to?(:receive_binary_data)
72
+ data = ""
73
+ end
74
+ end
75
+ end
76
+
77
+ def unbind
78
+ if @lbp_mode == :binary and @lbp_binary_limit > 0
79
+ if respond_to?(:receive_binary_data)
80
+ receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
81
+ end
82
+ end
83
+ end
84
+
85
+ # Set up to read the supplied number of binary bytes.
86
+ # This recycles all the data currently waiting in the line buffer, if any.
87
+ # If the limit is nil, then ALL subsequent data will be treated as binary
88
+ # data and passed to the upstream protocol handler as we receive it.
89
+ # If a limit is given, we'll hold the incoming binary data and not
90
+ # pass it upstream until we've seen it all, or until there is an unbind
91
+ # (in which case we'll pass up a partial).
92
+ # Specifying nil for the limit (the default) means there is no limit.
93
+ # Specifiyng zero for the limit will cause an immediate transition back to line mode.
94
+ #
95
+ def set_binary_mode size = nil
96
+ if @lbp_mode == :lines
97
+ if size == 0
98
+ receive_binary_data("") if respond_to?(:receive_binary_data)
99
+ # Do no more work here. Stay in line mode and keep consuming data.
100
+ else
101
+ @lbp_binary_limit = size.to_i # (nil will be stored as zero)
102
+ if @lbp_binary_limit > 0
103
+ raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
104
+ @lbp_binary_buffer = "\0" * @lbp_binary_limit
105
+ @lbp_binary_bytes_received = 0
106
+ end
107
+
108
+ @lbp_mode = :binary
109
+ receive_data @lpb_buffer.flush
110
+ end
111
+ else
112
+ raise "invalid operation"
113
+ end
114
+ end
115
+
116
+ #--
117
+ # For internal use, establish protocol baseline for handling lines.
118
+ def lbp_init_line_state
119
+ @lpb_buffer = BufferedTokenizer.new("\n")
120
+ @lbp_mode = :lines
121
+ end
122
+ private :lbp_init_line_state
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,29 @@
1
+ module EventMachine
2
+ module Protocols
3
+ # LineProtocol will parse out newline terminated strings from a receive_data stream
4
+ #
5
+ # module Server
6
+ # include EM::P::LineProtocol
7
+ #
8
+ # def receive_line(line)
9
+ # send_data("you said: #{line}")
10
+ # end
11
+ # end
12
+ #
13
+ module LineProtocol
14
+ # @private
15
+ def receive_data data
16
+ (@buf ||= '') << data
17
+
18
+ while @buf.slice!(/(.*?)\r?\n/)
19
+ receive_line($1)
20
+ end
21
+ end
22
+
23
+ # Invoked with lines received over the network
24
+ def receive_line(line)
25
+ # stub
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,166 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+ module EventMachine
27
+ module Protocols
28
+ # In the grand, time-honored tradition of re-inventing the wheel, we offer
29
+ # here YET ANOTHER protocol that handles line-oriented data with interspersed
30
+ # binary text. This one trades away some of the performance optimizations of
31
+ # EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
32
+ # with regard to binary text blocks that can switch back to line mode. It also
33
+ # permits the line-delimiter to change in midstream.
34
+ # This was originally written to support Stomp.
35
+ module LineText2
36
+ # TODO! We're not enforcing the limits on header lengths and text-lengths.
37
+ # When we get around to that, call #receive_error if the user defined it, otherwise
38
+ # throw exceptions.
39
+
40
+ MaxBinaryLength = 32*1024*1024
41
+
42
+ #--
43
+ # Will loop internally until there's no data left to read.
44
+ # That way the user-defined handlers we call can modify the
45
+ # handling characteristics on a per-token basis.
46
+ #
47
+ def receive_data data
48
+ return unless (data and data.length > 0)
49
+
50
+ # Do this stuff in lieu of a constructor.
51
+ @lt2_mode ||= :lines
52
+ @lt2_delimiter ||= "\n"
53
+ @lt2_linebuffer ||= []
54
+
55
+ remaining_data = data
56
+
57
+ while remaining_data.length > 0
58
+ if @lt2_mode == :lines
59
+ if ix = remaining_data.index( @lt2_delimiter )
60
+ @lt2_linebuffer << remaining_data[0...ix]
61
+ ln = @lt2_linebuffer.join
62
+ @lt2_linebuffer.clear
63
+ if @lt2_delimiter == "\n"
64
+ ln.chomp!
65
+ end
66
+ receive_line ln
67
+ remaining_data = remaining_data[(ix+@lt2_delimiter.length)..-1]
68
+ else
69
+ @lt2_linebuffer << remaining_data
70
+ remaining_data = ""
71
+ end
72
+ elsif @lt2_mode == :text
73
+ if @lt2_textsize
74
+ needed = @lt2_textsize - @lt2_textpos
75
+ will_take = if remaining_data.length > needed
76
+ needed
77
+ else
78
+ remaining_data.length
79
+ end
80
+
81
+ @lt2_textbuffer << remaining_data[0...will_take]
82
+ tail = remaining_data[will_take..-1]
83
+
84
+ @lt2_textpos += will_take
85
+ if @lt2_textpos >= @lt2_textsize
86
+ # Reset line mode (the default behavior) BEFORE calling the
87
+ # receive_binary_data. This makes it possible for user code
88
+ # to call set_text_mode, enabling chains of text blocks
89
+ # (which can possibly be of different sizes).
90
+ set_line_mode
91
+ receive_binary_data @lt2_textbuffer.join
92
+ receive_end_of_binary_data
93
+ end
94
+
95
+ remaining_data = tail
96
+ else
97
+ receive_binary_data remaining_data
98
+ remaining_data = ""
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+
105
+ def set_delimiter delim
106
+ @lt2_delimiter = delim.to_s
107
+ end
108
+
109
+ # Called internally but also exposed to user code, for the case in which
110
+ # processing of binary data creates a need to transition back to line mode.
111
+ # We support an optional parameter to "throw back" some data, which might
112
+ # be an umprocessed chunk of the transmitted binary data, or something else
113
+ # entirely.
114
+ def set_line_mode data=""
115
+ @lt2_mode = :lines
116
+ (@lt2_linebuffer ||= []).clear
117
+ receive_data data.to_s
118
+ end
119
+
120
+ def set_text_mode size=nil
121
+ if size == 0
122
+ set_line_mode
123
+ else
124
+ @lt2_mode = :text
125
+ (@lt2_textbuffer ||= []).clear
126
+ @lt2_textsize = size # which can be nil, signifying no limit
127
+ @lt2_textpos = 0
128
+ end
129
+ end
130
+
131
+ # Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
132
+ def set_binary_mode size=nil
133
+ set_text_mode size
134
+ end
135
+
136
+ # In case of a dropped connection, we'll send a partial buffer to user code
137
+ # when in sized text mode. User overrides of #receive_binary_data need to
138
+ # be aware that they may get a short buffer.
139
+ def unbind
140
+ @lt2_mode ||= nil
141
+ if @lt2_mode == :text and @lt2_textpos > 0
142
+ receive_binary_data @lt2_textbuffer.join
143
+ end
144
+ end
145
+
146
+ # Stub. Should be subclassed by user code.
147
+ def receive_line ln
148
+ # no-op
149
+ end
150
+
151
+ # Stub. Should be subclassed by user code.
152
+ def receive_binary_data data
153
+ # no-op
154
+ end
155
+
156
+ # Stub. Should be subclassed by user code.
157
+ # This is called when transitioning internally from text mode
158
+ # back to line mode. Useful when client code doesn't want
159
+ # to keep track of how much data it's received.
160
+ def receive_end_of_binary_data
161
+ # no-op
162
+ end
163
+ end
164
+ end
165
+ end
166
+
@@ -0,0 +1,331 @@
1
+ module EventMachine
2
+ module Protocols
3
+ # Implements the Memcache protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt).
4
+ # Requires memcached >= 1.2.4 w/ noreply support
5
+ #
6
+ # == Usage example
7
+ #
8
+ # EM.run{
9
+ # cache = EM::P::Memcache.connect 'localhost', 11211
10
+ #
11
+ # cache.set :a, 'hello'
12
+ # cache.set :b, 'hi'
13
+ # cache.set :c, 'how are you?'
14
+ # cache.set :d, ''
15
+ #
16
+ # cache.get(:a){ |v| p v }
17
+ # cache.get_hash(:a, :b, :c, :d){ |v| p v }
18
+ # cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
19
+ #
20
+ # cache.get(:a,:z,:b,:y,:d){ |a,z,b,y,d| p [a,z,b,y,d] }
21
+ #
22
+ # cache.get(:missing){ |m| p [:missing=, m] }
23
+ # cache.set(:missing, 'abc'){ p :stored }
24
+ # cache.get(:missing){ |m| p [:missing=, m] }
25
+ # cache.del(:missing){ p :deleted }
26
+ # cache.get(:missing){ |m| p [:missing=, m] }
27
+ # }
28
+ #
29
+ module Memcache
30
+ include EM::Deferrable
31
+
32
+ ##
33
+ # constants
34
+
35
+ unless defined? Cempty
36
+ # @private
37
+ Cstored = 'STORED'.freeze
38
+ # @private
39
+ Cend = 'END'.freeze
40
+ # @private
41
+ Cdeleted = 'DELETED'.freeze
42
+ # @private
43
+ Cunknown = 'NOT_FOUND'.freeze
44
+ # @private
45
+ Cerror = 'ERROR'.freeze
46
+
47
+ # @private
48
+ Cempty = ''.freeze
49
+ # @private
50
+ Cdelimiter = "\r\n".freeze
51
+ end
52
+
53
+ ##
54
+ # commands
55
+
56
+ # Get the value associated with one or multiple keys
57
+ #
58
+ # cache.get(:a){ |v| p v }
59
+ # cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
60
+ #
61
+ def get *keys
62
+ raise ArgumentError unless block_given?
63
+
64
+ callback{
65
+ keys = keys.map{|k| k.to_s.gsub(/\s/,'_') }
66
+ send_data "get #{keys.join(' ')}\r\n"
67
+ @get_cbs << [keys, proc{ |values|
68
+ yield *keys.map{ |k| values[k] }
69
+ }]
70
+ }
71
+ end
72
+
73
+ # Set the value for a given key
74
+ #
75
+ # cache.set :a, 'hello'
76
+ # cache.set(:missing, 'abc'){ puts "stored the value!" }
77
+ #
78
+ def set key, val, exptime = 0, &cb
79
+ callback{
80
+ val = val.to_s
81
+ send_cmd :set, key, 0, exptime, val.respond_to?(:bytesize) ? val.bytesize : val.size, !block_given?
82
+ send_data val
83
+ send_data Cdelimiter
84
+ @set_cbs << cb if cb
85
+ }
86
+ end
87
+
88
+ # Gets multiple values as a hash
89
+ #
90
+ # cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }
91
+ #
92
+ def get_hash *keys
93
+ raise ArgumentError unless block_given?
94
+
95
+ get *keys do |*values|
96
+ yield keys.inject({}){ |hash, k| hash.update k => values[keys.index(k)] }
97
+ end
98
+ end
99
+
100
+ # Delete the value associated with a key
101
+ #
102
+ # cache.del :a
103
+ # cache.del(:b){ puts "deleted the value!" }
104
+ #
105
+ def delete key, expires = 0, &cb
106
+ callback{
107
+ send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"
108
+ @del_cbs << cb if cb
109
+ }
110
+ end
111
+ alias del delete
112
+
113
+ # Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
114
+ def self.connect host = 'localhost', port = 11211
115
+ EM.connect host, port, self, host, port
116
+ end
117
+
118
+ def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false
119
+ send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
120
+ end
121
+ private :send_cmd
122
+
123
+ ##
124
+ # errors
125
+
126
+ # @private
127
+ class ParserError < StandardError
128
+ end
129
+
130
+ ##
131
+ # em hooks
132
+
133
+ # @private
134
+ def initialize host, port = 11211
135
+ @host, @port = host, port
136
+ end
137
+
138
+ # @private
139
+ def connection_completed
140
+ @get_cbs = []
141
+ @set_cbs = []
142
+ @del_cbs = []
143
+
144
+ @values = {}
145
+
146
+ @reconnecting = false
147
+ @connected = true
148
+ succeed
149
+ # set_delimiter "\r\n"
150
+ # set_line_mode
151
+ end
152
+
153
+ #--
154
+ # 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
155
+ # stack overflows when there is too much data.
156
+ # include EM::P::LineText2
157
+ # @private
158
+ def receive_data data
159
+ (@buffer||='') << data
160
+
161
+ while index = @buffer.index(Cdelimiter)
162
+ begin
163
+ line = @buffer.slice!(0,index+2)
164
+ process_cmd line
165
+ rescue ParserError
166
+ @buffer[0...0] = line
167
+ break
168
+ end
169
+ end
170
+ end
171
+
172
+ #--
173
+ # def receive_line line
174
+ # @private
175
+ def process_cmd line
176
+ case line.strip
177
+ when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>
178
+ bytes = Integer($3)
179
+ # set_binary_mode bytes+2
180
+ # @cur_key = $1
181
+ if @buffer.size >= bytes + 2
182
+ @values[$1] = @buffer.slice!(0,bytes)
183
+ @buffer.slice!(0,2) # \r\n
184
+ else
185
+ raise ParserError
186
+ end
187
+
188
+ when Cend # END
189
+ if entry = @get_cbs.shift
190
+ keys, cb = entry
191
+ cb.call(@values)
192
+ end
193
+ @values = {}
194
+
195
+ when Cstored # STORED
196
+ if cb = @set_cbs.shift
197
+ cb.call(true)
198
+ end
199
+
200
+ when Cdeleted # DELETED
201
+ if cb = @del_cbs.shift
202
+ cb.call(true)
203
+ end
204
+
205
+ when Cunknown # NOT_FOUND
206
+ if cb = @del_cbs.shift
207
+ cb.call(false)
208
+ end
209
+
210
+ else
211
+ p [:MEMCACHE_UNKNOWN, line]
212
+ end
213
+ end
214
+
215
+ #--
216
+ # def receive_binary_data data
217
+ # @values[@cur_key] = data[0..-3]
218
+ # end
219
+
220
+ # @private
221
+ def unbind
222
+ if @connected or @reconnecting
223
+ EM.add_timer(1){ reconnect @host, @port }
224
+ @connected = false
225
+ @reconnecting = true
226
+ @deferred_status = nil
227
+ else
228
+ raise 'Unable to connect to memcached server'
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ if __FILE__ == $0
236
+ # ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
237
+ require 'em/spec'
238
+
239
+ # @private
240
+ class TestConnection
241
+ include EM::P::Memcache
242
+ def send_data data
243
+ sent_data << data
244
+ end
245
+ def sent_data
246
+ @sent_data ||= ''
247
+ end
248
+
249
+ def initialize
250
+ connection_completed
251
+ end
252
+ end
253
+
254
+ EM.describe EM::Protocols::Memcache do
255
+
256
+ before{
257
+ @c = TestConnection.new
258
+ }
259
+
260
+ should 'send get requests' do
261
+ @c.get('a'){}
262
+ @c.sent_data.should == "get a\r\n"
263
+ done
264
+ end
265
+
266
+ should 'send set requests' do
267
+ @c.set('a', 1){}
268
+ @c.sent_data.should == "set a 0 0 1\r\n1\r\n"
269
+ done
270
+ end
271
+
272
+ should 'use noreply on set without block' do
273
+ @c.set('a', 1)
274
+ @c.sent_data.should == "set a 0 0 1 noreply\r\n1\r\n"
275
+ done
276
+ end
277
+
278
+ should 'send delete requests' do
279
+ @c.del('a')
280
+ @c.sent_data.should == "delete a 0 noreply\r\n"
281
+ done
282
+ end
283
+
284
+ should 'work when get returns no values' do
285
+ @c.get('a'){ |a|
286
+ a.should.be.nil
287
+ done
288
+ }
289
+
290
+ @c.receive_data "END\r\n"
291
+ end
292
+
293
+ should 'invoke block on set' do
294
+ @c.set('a', 1){
295
+ done
296
+ }
297
+
298
+ @c.receive_data "STORED\r\n"
299
+ end
300
+
301
+ should 'invoke block on delete' do
302
+ @c.delete('a'){ |found|
303
+ found.should.be.false
304
+ }
305
+ @c.delete('b'){ |found|
306
+ found.should.be.true
307
+ done
308
+ }
309
+
310
+ @c.receive_data "NOT_FOUND\r\n"
311
+ @c.receive_data "DELETED\r\n"
312
+ end
313
+
314
+ should 'parse split responses' do
315
+ @c.get('a'){ |a|
316
+ a.should == 'abc'
317
+ done
318
+ }
319
+
320
+ @c.receive_data "VAL"
321
+ @c.receive_data "UE a 0 "
322
+ @c.receive_data "3\r\n"
323
+ @c.receive_data "ab"
324
+ @c.receive_data "c"
325
+ @c.receive_data "\r\n"
326
+ @c.receive_data "EN"
327
+ @c.receive_data "D\r\n"
328
+ end
329
+
330
+ end
331
+ end