sensu-em 2.4.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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