sensu-em 2.4.0-x86-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 (178) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.travis.yml +12 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +33 -0
  6. data/GNU +281 -0
  7. data/Gemfile +2 -0
  8. data/LICENSE +60 -0
  9. data/README.md +109 -0
  10. data/Rakefile +20 -0
  11. data/docs/DocumentationGuidesIndex.md +27 -0
  12. data/docs/GettingStarted.md +521 -0
  13. data/docs/old/ChangeLog +211 -0
  14. data/docs/old/DEFERRABLES +246 -0
  15. data/docs/old/EPOLL +141 -0
  16. data/docs/old/INSTALL +13 -0
  17. data/docs/old/KEYBOARD +42 -0
  18. data/docs/old/LEGAL +25 -0
  19. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  20. data/docs/old/PURE_RUBY +75 -0
  21. data/docs/old/RELEASE_NOTES +94 -0
  22. data/docs/old/SMTP +4 -0
  23. data/docs/old/SPAWNED_PROCESSES +148 -0
  24. data/docs/old/TODO +8 -0
  25. data/eventmachine.gemspec +37 -0
  26. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  27. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  28. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  29. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  30. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  31. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  32. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  33. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  34. data/examples/old/ex_channel.rb +43 -0
  35. data/examples/old/ex_queue.rb +2 -0
  36. data/examples/old/ex_tick_loop_array.rb +15 -0
  37. data/examples/old/ex_tick_loop_counter.rb +32 -0
  38. data/examples/old/helper.rb +2 -0
  39. data/ext/binder.cpp +124 -0
  40. data/ext/binder.h +46 -0
  41. data/ext/cmain.cpp +887 -0
  42. data/ext/ed.cpp +1992 -0
  43. data/ext/ed.h +424 -0
  44. data/ext/em.cpp +2352 -0
  45. data/ext/em.h +253 -0
  46. data/ext/eventmachine.h +128 -0
  47. data/ext/extconf.rb +179 -0
  48. data/ext/fastfilereader/extconf.rb +103 -0
  49. data/ext/fastfilereader/mapper.cpp +214 -0
  50. data/ext/fastfilereader/mapper.h +59 -0
  51. data/ext/fastfilereader/rubymain.cpp +127 -0
  52. data/ext/kb.cpp +79 -0
  53. data/ext/page.cpp +107 -0
  54. data/ext/page.h +51 -0
  55. data/ext/pipe.cpp +347 -0
  56. data/ext/project.h +161 -0
  57. data/ext/rubymain.cpp +1318 -0
  58. data/ext/ssl.cpp +476 -0
  59. data/ext/ssl.h +95 -0
  60. data/java/.classpath +6 -0
  61. data/java/.gitignore +1 -0
  62. data/java/.project +17 -0
  63. data/java/src/com/rubyeventmachine/DatagramPacket.java +13 -0
  64. data/java/src/com/rubyeventmachine/EmReactor.java +531 -0
  65. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  66. data/java/src/com/rubyeventmachine/EventCallback.java +7 -0
  67. data/java/src/com/rubyeventmachine/EventCode.java +26 -0
  68. data/java/src/com/rubyeventmachine/EventableChannel.java +130 -0
  69. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +179 -0
  70. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +405 -0
  71. data/java/src/com/rubyeventmachine/SslBox.java +311 -0
  72. data/lib/em/buftok.rb +110 -0
  73. data/lib/em/callback.rb +58 -0
  74. data/lib/em/channel.rb +64 -0
  75. data/lib/em/completion.rb +304 -0
  76. data/lib/em/connection.rb +716 -0
  77. data/lib/em/deferrable.rb +210 -0
  78. data/lib/em/deferrable/pool.rb +2 -0
  79. data/lib/em/file_watch.rb +73 -0
  80. data/lib/em/future.rb +61 -0
  81. data/lib/em/iterator.rb +231 -0
  82. data/lib/em/messages.rb +66 -0
  83. data/lib/em/pool.rb +151 -0
  84. data/lib/em/process_watch.rb +45 -0
  85. data/lib/em/processes.rb +123 -0
  86. data/lib/em/protocols.rb +37 -0
  87. data/lib/em/protocols/header_and_content.rb +138 -0
  88. data/lib/em/protocols/httpclient.rb +279 -0
  89. data/lib/em/protocols/httpclient2.rb +600 -0
  90. data/lib/em/protocols/line_and_text.rb +125 -0
  91. data/lib/em/protocols/line_protocol.rb +29 -0
  92. data/lib/em/protocols/linetext2.rb +161 -0
  93. data/lib/em/protocols/memcache.rb +331 -0
  94. data/lib/em/protocols/object_protocol.rb +46 -0
  95. data/lib/em/protocols/postgres3.rb +246 -0
  96. data/lib/em/protocols/saslauth.rb +175 -0
  97. data/lib/em/protocols/smtpclient.rb +365 -0
  98. data/lib/em/protocols/smtpserver.rb +643 -0
  99. data/lib/em/protocols/socks4.rb +66 -0
  100. data/lib/em/protocols/stomp.rb +205 -0
  101. data/lib/em/protocols/tcptest.rb +54 -0
  102. data/lib/em/pure_ruby.rb +1017 -0
  103. data/lib/em/queue.rb +71 -0
  104. data/lib/em/resolver.rb +209 -0
  105. data/lib/em/spawnable.rb +84 -0
  106. data/lib/em/streamer.rb +118 -0
  107. data/lib/em/threaded_resource.rb +90 -0
  108. data/lib/em/tick_loop.rb +85 -0
  109. data/lib/em/timers.rb +61 -0
  110. data/lib/em/version.rb +3 -0
  111. data/lib/eventmachine.rb +1553 -0
  112. data/lib/fastfilereaderext.rb +2 -0
  113. data/lib/jeventmachine.rb +321 -0
  114. data/lib/rubyeventmachine.rb +2 -0
  115. data/rakelib/cpp.rake_example +77 -0
  116. data/rakelib/package.rake +98 -0
  117. data/rakelib/test.rake +8 -0
  118. data/tests/client.crt +31 -0
  119. data/tests/client.key +51 -0
  120. data/tests/em_test_helper.rb +64 -0
  121. data/tests/server.crt +36 -0
  122. data/tests/server.key +51 -0
  123. data/tests/test_attach.rb +150 -0
  124. data/tests/test_basic.rb +294 -0
  125. data/tests/test_channel.rb +62 -0
  126. data/tests/test_completion.rb +177 -0
  127. data/tests/test_connection_count.rb +53 -0
  128. data/tests/test_defer.rb +18 -0
  129. data/tests/test_deferrable.rb +35 -0
  130. data/tests/test_epoll.rb +145 -0
  131. data/tests/test_error_handler.rb +38 -0
  132. data/tests/test_exc.rb +28 -0
  133. data/tests/test_file_watch.rb +65 -0
  134. data/tests/test_futures.rb +170 -0
  135. data/tests/test_get_sock_opt.rb +37 -0
  136. data/tests/test_handler_check.rb +35 -0
  137. data/tests/test_hc.rb +155 -0
  138. data/tests/test_httpclient.rb +190 -0
  139. data/tests/test_httpclient2.rb +133 -0
  140. data/tests/test_idle_connection.rb +25 -0
  141. data/tests/test_inactivity_timeout.rb +54 -0
  142. data/tests/test_iterator.rb +97 -0
  143. data/tests/test_kb.rb +34 -0
  144. data/tests/test_line_protocol.rb +33 -0
  145. data/tests/test_ltp.rb +138 -0
  146. data/tests/test_ltp2.rb +288 -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 +102 -0
  150. data/tests/test_pending_connect_timeout.rb +52 -0
  151. data/tests/test_pool.rb +194 -0
  152. data/tests/test_process_watch.rb +48 -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 +50 -0
  157. data/tests/test_resolver.rb +55 -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 +37 -0
  163. data/tests/test_shutdown_hooks.rb +23 -0
  164. data/tests/test_smtpclient.rb +55 -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_echo_data.rb +60 -0
  169. data/tests/test_ssl_methods.rb +56 -0
  170. data/tests/test_ssl_verify.rb +82 -0
  171. data/tests/test_stomp.rb +37 -0
  172. data/tests/test_system.rb +42 -0
  173. data/tests/test_threaded_resource.rb +53 -0
  174. data/tests/test_tick_loop.rb +59 -0
  175. data/tests/test_timers.rb +123 -0
  176. data/tests/test_ud.rb +8 -0
  177. data/tests/test_unbind_reason.rb +48 -0
  178. metadata +300 -0
@@ -0,0 +1,311 @@
1
+ package com.rubyeventmachine;
2
+
3
+ import java.io.IOException;
4
+ import java.nio.ByteBuffer;
5
+ import java.nio.channels.SelectionKey;
6
+ import java.nio.channels.SocketChannel;
7
+ import java.security.KeyManagementException;
8
+ import java.security.KeyStore;
9
+ import java.security.KeyStoreException;
10
+ import java.security.NoSuchAlgorithmException;
11
+ import java.security.UnrecoverableKeyException;
12
+
13
+ import javax.net.ssl.KeyManager;
14
+ import javax.net.ssl.KeyManagerFactory;
15
+ import javax.net.ssl.SSLContext;
16
+ import javax.net.ssl.SSLEngine;
17
+ import javax.net.ssl.SSLEngineResult;
18
+ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
19
+ import javax.net.ssl.SSLEngineResult.Status;
20
+ import javax.net.ssl.SSLPeerUnverifiedException;
21
+ import javax.net.ssl.TrustManager;
22
+ import javax.net.ssl.X509TrustManager;
23
+
24
+ public class SslBox {
25
+
26
+ private final SSLContext sslContext;
27
+ private final SSLEngine sslEngine;
28
+
29
+ private final ByteBuffer netInBuffer;
30
+ private final ByteBuffer netOutBuffer;
31
+ private final ByteBuffer anotherBuffer;
32
+
33
+ public static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
34
+ private final SocketChannel channel;
35
+
36
+ private boolean handshakeComplete;
37
+ protected HandshakeStatus handshakeStatus; //gets set by handshake
38
+
39
+ public SslBox(boolean isServer, SocketChannel channel, KeyStore keyStore, X509TrustManager tm, boolean verifyPeer, String host, int port) {
40
+ try {
41
+ sslContext = SSLContext.getInstance("TLS");
42
+ KeyManager[] keyManagers = null;
43
+
44
+ if (keyStore != null) {
45
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
46
+ kmf.init(keyStore, null);
47
+ keyManagers = kmf.getKeyManagers();
48
+ }
49
+
50
+ sslContext.init(keyManagers, new TrustManager[] { tm }, null);
51
+ sslEngine = sslContext.createSSLEngine(host, port);
52
+ sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
53
+ sslEngine.setUseClientMode(!isServer);
54
+ sslEngine.setNeedClientAuth(verifyPeer);
55
+
56
+ this.channel = channel;
57
+
58
+ int netBufSize = sslEngine.getSession().getPacketBufferSize();
59
+ netInBuffer = ByteBuffer.allocate(netBufSize);
60
+ netOutBuffer = ByteBuffer.allocate(netBufSize);
61
+ anotherBuffer = ByteBuffer.allocate(netBufSize);
62
+ reset();
63
+ } catch (NoSuchAlgorithmException e) {
64
+ throw new RuntimeException("unable to start TLS: " + e.getMessage(), e);
65
+ } catch (UnrecoverableKeyException e) {
66
+ throw new RuntimeException("unable to start TLS: " + e.getMessage(), e);
67
+ } catch (KeyStoreException e) {
68
+ throw new RuntimeException("unable to start TLS: " + e.getMessage(), e);
69
+ } catch (KeyManagementException e) {
70
+ throw new RuntimeException("unable to start TLS: " + e.getMessage(), e);
71
+ } catch (IOException e) {
72
+ throw new RuntimeException("unable to start TLS: " + e.getMessage(), e);
73
+ }
74
+ }
75
+
76
+ public void reset() throws IOException {
77
+ netOutBuffer.position(0);
78
+ netOutBuffer.limit(0);
79
+ netInBuffer.position(0);
80
+ netInBuffer.limit(0);
81
+ handshakeComplete = false;
82
+ //initiate handshake
83
+ sslEngine.beginHandshake();
84
+ handshakeStatus = sslEngine.getHandshakeStatus();
85
+ }
86
+
87
+ public boolean handshake(SelectionKey channelKey) {
88
+ try {
89
+ int newOps = do_handshake(channelKey.isReadable(), channelKey.isWritable());
90
+ channelKey.interestOps(newOps);
91
+ } catch (IOException e) {
92
+ return false;
93
+ }
94
+ return true;
95
+ }
96
+
97
+ public boolean handshakeNeeded() {
98
+ return !handshakeComplete;
99
+ }
100
+
101
+ private int do_handshake(boolean read, boolean write) throws IOException {
102
+ if (!flush(netOutBuffer)) return SelectionKey.OP_WRITE; //we still have data to write
103
+
104
+ SSLEngineResult handshake = null;
105
+
106
+ while (!handshakeComplete) {
107
+ switch ( handshakeStatus ) {
108
+ case FINISHED: {
109
+ //we are complete if we have delivered the last package
110
+ handshakeComplete = !netOutBuffer.hasRemaining();
111
+ //return 0 if we are complete, otherwise we still have data to write
112
+ return handshakeComplete?0:SelectionKey.OP_WRITE;
113
+ }
114
+ case NEED_WRAP: {
115
+ //perform the wrap function
116
+ handshake = handshakeWrap(write);
117
+ if ( handshake.getStatus() == Status.OK ){
118
+ if (handshakeStatus == HandshakeStatus.NEED_TASK)
119
+ handshakeStatus = tasks();
120
+ } else {
121
+ //wrap should always work with our buffers
122
+ throw new IOException("Unexpected status:" + handshake.getStatus() + " during handshake WRAP.");
123
+ }
124
+ if ( handshakeStatus != HandshakeStatus.NEED_UNWRAP || (!flush(netOutBuffer)) ) {
125
+ //should actually return OP_READ if we have NEED_UNWRAP
126
+ return SelectionKey.OP_WRITE;
127
+ }
128
+ //fall down to NEED_UNWRAP on the same call, will result in a
129
+ //BUFFER_UNDERFLOW if it needs data
130
+ }
131
+ //$FALL-THROUGH$
132
+ case NEED_UNWRAP: {
133
+ //perform the unwrap function
134
+ handshake = handshakeUnwrap(read);
135
+ if ( handshake.getStatus() == Status.OK ) {
136
+ if (handshakeStatus == HandshakeStatus.NEED_TASK)
137
+ handshakeStatus = tasks();
138
+ } else if ( handshake.getStatus() == Status.BUFFER_UNDERFLOW ){
139
+ //read more data, reregister for OP_READ
140
+ return SelectionKey.OP_READ;
141
+ } else {
142
+ throw new IOException("Invalid handshake status:"+handshakeStatus+" during handshake UNWRAP.");
143
+ }//switch
144
+ break;
145
+ }
146
+ case NEED_TASK: {
147
+ handshakeStatus = tasks();
148
+ break;
149
+ }
150
+ default: throw new IllegalStateException("Invalid handshake status:"+handshakeStatus);
151
+ }//switch
152
+ }//while
153
+ //return 0 if we are complete, otherwise reregister for any activity that
154
+ //would cause this method to be called again.
155
+ return handshakeComplete?0:(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
156
+ }
157
+
158
+
159
+ /**
160
+ * Performs the WRAP function
161
+ * @param doWrite boolean
162
+ * @return SSLEngineResult
163
+ * @throws IOException
164
+ */
165
+ private SSLEngineResult handshakeWrap(boolean doWrite) throws IOException {
166
+ //this should never be called with a network buffer that contains data
167
+ //so we can clear it here.
168
+ netOutBuffer.clear();
169
+ //perform the wrap
170
+ SSLEngineResult result = sslEngine.wrap(emptyBuf, netOutBuffer);
171
+ //prepare the results to be written
172
+ netOutBuffer.flip();
173
+ //set the status
174
+ handshakeStatus = result.getHandshakeStatus();
175
+ //optimization, if we do have a writable channel, write it now
176
+ if ( doWrite ) flush(netOutBuffer);
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Perform handshake unwrap
182
+ * @param doread boolean
183
+ * @return SSLEngineResult
184
+ * @throws IOException
185
+ */
186
+ private SSLEngineResult handshakeUnwrap(boolean doread) throws IOException {
187
+
188
+ if (netInBuffer.position() == netInBuffer.limit()) {
189
+ //clear the buffer if we have emptied it out on data
190
+ netInBuffer.clear();
191
+ }
192
+ if ( doread ) {
193
+ //if we have data to read, read it
194
+ int read = channel.read(netInBuffer);
195
+ if (read == -1) throw new IOException("EOF encountered during handshake.");
196
+ }
197
+ SSLEngineResult result;
198
+ boolean cont = false;
199
+ //loop while we can perform pure SSLEngine data
200
+ do {
201
+ //prepare the buffer with the incoming data
202
+ netInBuffer.flip();
203
+ //call unwrap
204
+ result = sslEngine.unwrap(netInBuffer, anotherBuffer);
205
+ //compact the buffer, this is an optional method, wonder what would happen if we didn't
206
+ netInBuffer.compact();
207
+ //read in the status
208
+ handshakeStatus = result.getHandshakeStatus();
209
+ if ( result.getStatus() == SSLEngineResult.Status.OK &&
210
+ result.getHandshakeStatus() == HandshakeStatus.NEED_TASK ) {
211
+ //execute tasks if we need to
212
+ handshakeStatus = tasks();
213
+ }
214
+ //perform another unwrap?
215
+ cont = result.getStatus() == SSLEngineResult.Status.OK &&
216
+ handshakeStatus == HandshakeStatus.NEED_UNWRAP;
217
+ }while ( cont );
218
+ return result;
219
+ }
220
+
221
+ /**
222
+ * Executes all the tasks needed on the same thread.
223
+ * @return HandshakeStatus
224
+ */
225
+ private SSLEngineResult.HandshakeStatus tasks() {
226
+ Runnable r = null;
227
+ while ( (r = sslEngine.getDelegatedTask()) != null) {
228
+ r.run();
229
+ }
230
+ return sslEngine.getHandshakeStatus();
231
+ }
232
+
233
+ protected boolean flush(ByteBuffer buf) throws IOException {
234
+ int remaining = buf.remaining();
235
+ if ( remaining > 0 ) {
236
+ int written = channel.write(buf);
237
+ return written >= remaining;
238
+ }else {
239
+ return true;
240
+ }
241
+ }
242
+
243
+ public javax.security.cert.X509Certificate getPeerCert() throws SSLPeerUnverifiedException {
244
+ return sslEngine.getSession().getPeerCertificateChain()[0];
245
+ }
246
+
247
+ public int write(ByteBuffer src) throws IOException {
248
+ if (!flush(netOutBuffer)) return 0;
249
+
250
+ netOutBuffer.clear();
251
+
252
+ SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
253
+ int written = result.bytesConsumed();
254
+ netOutBuffer.flip();
255
+
256
+ if (result.getStatus() == Status.OK) {
257
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK)
258
+ tasks();
259
+ } else {
260
+ throw new IOException("Unable to wrap data, invalid engine state: " +result.getStatus());
261
+ }
262
+
263
+ //force a flush
264
+ flush(netOutBuffer);
265
+
266
+ return written;
267
+ }
268
+
269
+ public int read(ByteBuffer dst) throws IOException {
270
+ //did we finish our handshake?
271
+ if (!handshakeComplete) throw new IllegalStateException("Handshake incomplete, you must complete handshake before reading data.");
272
+
273
+ //read from the network
274
+ int netread = channel.read(netInBuffer);
275
+ //did we reach EOF? if so send EOF up one layer.
276
+ if (netread == -1) return -1;
277
+
278
+ //the data read
279
+ int read = 0;
280
+ //the SSL engine result
281
+ SSLEngineResult unwrap;
282
+ do {
283
+ //prepare the buffer
284
+ netInBuffer.flip();
285
+ //unwrap the data
286
+ unwrap = sslEngine.unwrap(netInBuffer, dst);
287
+ //compact the buffer
288
+ netInBuffer.compact();
289
+
290
+ if ( unwrap.getStatus()==Status.OK || unwrap.getStatus()==Status.BUFFER_UNDERFLOW ) {
291
+ //we did receive some data, add it to our total
292
+ read += unwrap.bytesProduced();
293
+ //perform any tasks if needed
294
+ if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
295
+ //if we need more network data, then bail out for now.
296
+ if ( unwrap.getStatus() == Status.BUFFER_UNDERFLOW ) break;
297
+ }else if ( unwrap.getStatus()==Status.BUFFER_OVERFLOW && read>0 ) {
298
+ //buffer overflow can happen, if we have read data, then
299
+ //empty out the dst buffer before we do another read
300
+ break;
301
+ }else {
302
+ //here we should trap BUFFER_OVERFLOW and call expand on the buffer
303
+ //for now, throw an exception, as we initialized the buffers
304
+ //in the constructor
305
+ throw new IOException("Unable to unwrap data, invalid status: " + unwrap.getStatus());
306
+ }
307
+ } while ( (netInBuffer.position() != 0)); //continue to unwrapping as long as the input buffer has stuff
308
+ return (read);
309
+ }
310
+
311
+ }
data/lib/em/buftok.rb ADDED
@@ -0,0 +1,110 @@
1
+ # BufferedTokenizer takes a delimiter upon instantiation, or acts line-based
2
+ # by default. It allows input to be spoon-fed from some outside source which
3
+ # receives arbitrary length datagrams which may-or-may-not contain the token
4
+ # by which entities are delimited.
5
+ #
6
+ # By default, new BufferedTokenizers will operate on lines delimited by "\n" by default
7
+ # or allow you to specify any delimiter token you so choose, which will then
8
+ # be used by String#split to tokenize the input data
9
+ #
10
+ # @example Using BufferedTokernizer to parse lines out of incoming data
11
+ #
12
+ # module LineBufferedConnection
13
+ # def receive_data(data)
14
+ # (@buffer ||= BufferedTokenizer.new).extract(data).each do |line|
15
+ # receive_line(line)
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ # @author Tony Arcieri
21
+ # @author Martin Emde
22
+ class BufferedTokenizer
23
+ # @param [String] delimiter
24
+ # @param [Integer] size_limit
25
+ def initialize(delimiter = "\n", size_limit = nil)
26
+ @delimiter = delimiter
27
+ @size_limit = size_limit
28
+
29
+ # The input buffer is stored as an array. This is by far the most efficient
30
+ # approach given language constraints (in C a linked list would be a more
31
+ # appropriate data structure). Segments of input data are stored in a list
32
+ # which is only joined when a token is reached, substantially reducing the
33
+ # number of objects required for the operation.
34
+ @input = []
35
+
36
+ # Size of the input buffer
37
+ @input_size = 0
38
+ end
39
+
40
+ # Extract takes an arbitrary string of input data and returns an array of
41
+ # tokenized entities, provided there were any available to extract.
42
+ #
43
+ # @example
44
+ #
45
+ # tokenizer.extract(data).
46
+ # map { |entity| Decode(entity) }.each { ... }
47
+ #
48
+ # @param [String] data
49
+ def extract(data)
50
+ # Extract token-delimited entities from the input string with the split command.
51
+ # There's a bit of craftiness here with the -1 parameter. Normally split would
52
+ # behave no differently regardless of if the token lies at the very end of the
53
+ # input buffer or not (i.e. a literal edge case) Specifying -1 forces split to
54
+ # return "" in this case, meaning that the last entry in the list represents a
55
+ # new segment of data where the token has not been encountered
56
+ entities = data.split @delimiter, -1
57
+
58
+ # Check to see if the buffer has exceeded capacity, if we're imposing a limit
59
+ if @size_limit
60
+ raise 'input buffer full' if @input_size + entities.first.size > @size_limit
61
+ @input_size += entities.first.size
62
+ end
63
+
64
+ # Move the first entry in the resulting array into the input buffer. It represents
65
+ # the last segment of a token-delimited entity unless it's the only entry in the list.
66
+ @input << entities.shift
67
+
68
+ # If the resulting array from the split is empty, the token was not encountered
69
+ # (not even at the end of the buffer). Since we've encountered no token-delimited
70
+ # entities this go-around, return an empty array.
71
+ return [] if entities.empty?
72
+
73
+ # At this point, we've hit a token, or potentially multiple tokens. Now we can bring
74
+ # together all the data we've buffered from earlier calls without hitting a token,
75
+ # and add it to our list of discovered entities.
76
+ entities.unshift @input.join
77
+
78
+ # Now that we've hit a token, joined the input buffer and added it to the entities
79
+ # list, we can go ahead and clear the input buffer. All of the segments that were
80
+ # stored before the join can now be garbage collected.
81
+ @input.clear
82
+
83
+ # The last entity in the list is not token delimited, however, thanks to the -1
84
+ # passed to split. It represents the beginning of a new list of as-yet-untokenized
85
+ # data, so we add it to the start of the list.
86
+ @input << entities.pop
87
+
88
+ # Set the new input buffer size, provided we're keeping track
89
+ @input_size = @input.first.size if @size_limit
90
+
91
+ # Now we're left with the list of extracted token-delimited entities we wanted
92
+ # in the first place. Hooray!
93
+ entities
94
+ end
95
+
96
+ # Flush the contents of the input buffer, i.e. return the input buffer even though
97
+ # a token has not yet been encountered.
98
+ #
99
+ # @return [String]
100
+ def flush
101
+ buffer = @input.join
102
+ @input.clear
103
+ buffer
104
+ end
105
+
106
+ # @return [Boolean]
107
+ def empty?
108
+ @input.empty?
109
+ end
110
+ end
@@ -0,0 +1,58 @@
1
+ module EventMachine
2
+ # Utility method for coercing arguments to an object that responds to :call.
3
+ # Accepts an object and a method name to send to, or a block, or an object
4
+ # that responds to :call.
5
+ #
6
+ # @example EventMachine.Callback used with a block. Returns that block.
7
+ #
8
+ # cb = EventMachine.Callback do |msg|
9
+ # puts(msg)
10
+ # end
11
+ # # returned object is a callable
12
+ # cb.call('hello world')
13
+ #
14
+ #
15
+ # @example EventMachine.Callback used with an object (to be more specific, class object) and a method name, returns an object that responds to #call
16
+ #
17
+ # cb = EventMachine.Callback(Object, :puts)
18
+ # # returned object is a callable that delegates to Kernel#puts (in this case Object.puts)
19
+ # cb.call('hello world')
20
+ #
21
+ #
22
+ # @example EventMachine.Callback used with an object that responds to #call. Returns the argument.
23
+ #
24
+ # cb = EventMachine.Callback(proc{ |msg| puts(msg) })
25
+ # # returned object is a callable
26
+ # cb.call('hello world')
27
+ #
28
+ #
29
+ # @overload Callback(object, method)
30
+ # Wraps `method` invocation on `object` into an object that responds to #call that proxies all the arguments to that method
31
+ # @param [Object] Object to invoke method on
32
+ # @param [Symbol] Method name
33
+ # @return [<#call>] An object that responds to #call that takes any number of arguments and invokes method on object with those arguments
34
+ #
35
+ # @overload Callback(object)
36
+ # Returns callable object as is, without any coercion
37
+ # @param [<#call>] An object that responds to #call
38
+ # @return [<#call>] Its argument
39
+ #
40
+ # @overload Callback(&block)
41
+ # Returns block passed to it without any coercion
42
+ # @return [<#call>] Block passed to this method
43
+ #
44
+ # @raise [ArgumentError] When argument doesn't respond to #call, method name is missing or when invoked without arguments and block isn't given
45
+ #
46
+ # @return [<#call>]
47
+ def self.Callback(object = nil, method = nil, &blk)
48
+ if object && method
49
+ lambda { |*args| object.__send__ method, *args }
50
+ else
51
+ if object.respond_to? :call
52
+ object
53
+ else
54
+ blk || raise(ArgumentError)
55
+ end # if
56
+ end # if
57
+ end # self.Callback
58
+ end # EventMachine