sensu-em 2.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (177) 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 +38 -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 +1988 -0
  43. data/ext/ed.h +422 -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 +468 -0
  59. data/ext/ssl.h +94 -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 +529 -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 +180 -0
  70. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +405 -0
  71. data/java/src/com/rubyeventmachine/SslBox.java +310 -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 +712 -0
  77. data/lib/em/deferrable/pool.rb +2 -0
  78. data/lib/em/deferrable.rb +210 -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/header_and_content.rb +138 -0
  87. data/lib/em/protocols/httpclient.rb +279 -0
  88. data/lib/em/protocols/httpclient2.rb +600 -0
  89. data/lib/em/protocols/line_and_text.rb +125 -0
  90. data/lib/em/protocols/line_protocol.rb +29 -0
  91. data/lib/em/protocols/linetext2.rb +161 -0
  92. data/lib/em/protocols/memcache.rb +331 -0
  93. data/lib/em/protocols/object_protocol.rb +46 -0
  94. data/lib/em/protocols/postgres3.rb +246 -0
  95. data/lib/em/protocols/saslauth.rb +175 -0
  96. data/lib/em/protocols/smtpclient.rb +365 -0
  97. data/lib/em/protocols/smtpserver.rb +643 -0
  98. data/lib/em/protocols/socks4.rb +66 -0
  99. data/lib/em/protocols/stomp.rb +205 -0
  100. data/lib/em/protocols/tcptest.rb +54 -0
  101. data/lib/em/protocols.rb +37 -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/jeventmachine.rb +321 -0
  113. data/lib/rubyeventmachine.jar +0 -0
  114. data/rakelib/cpp.rake_example +77 -0
  115. data/rakelib/package.rake +98 -0
  116. data/rakelib/test.rake +8 -0
  117. data/tests/client.crt +31 -0
  118. data/tests/client.key +51 -0
  119. data/tests/em_test_helper.rb +64 -0
  120. data/tests/server.crt +36 -0
  121. data/tests/server.key +51 -0
  122. data/tests/test_attach.rb +150 -0
  123. data/tests/test_basic.rb +294 -0
  124. data/tests/test_channel.rb +62 -0
  125. data/tests/test_completion.rb +177 -0
  126. data/tests/test_connection_count.rb +53 -0
  127. data/tests/test_defer.rb +18 -0
  128. data/tests/test_deferrable.rb +35 -0
  129. data/tests/test_epoll.rb +145 -0
  130. data/tests/test_error_handler.rb +38 -0
  131. data/tests/test_exc.rb +28 -0
  132. data/tests/test_file_watch.rb +65 -0
  133. data/tests/test_futures.rb +170 -0
  134. data/tests/test_get_sock_opt.rb +37 -0
  135. data/tests/test_handler_check.rb +35 -0
  136. data/tests/test_hc.rb +155 -0
  137. data/tests/test_httpclient.rb +190 -0
  138. data/tests/test_httpclient2.rb +133 -0
  139. data/tests/test_idle_connection.rb +25 -0
  140. data/tests/test_inactivity_timeout.rb +54 -0
  141. data/tests/test_iterator.rb +97 -0
  142. data/tests/test_kb.rb +34 -0
  143. data/tests/test_line_protocol.rb +33 -0
  144. data/tests/test_ltp.rb +138 -0
  145. data/tests/test_ltp2.rb +288 -0
  146. data/tests/test_next_tick.rb +104 -0
  147. data/tests/test_object_protocol.rb +36 -0
  148. data/tests/test_pause.rb +102 -0
  149. data/tests/test_pending_connect_timeout.rb +52 -0
  150. data/tests/test_pool.rb +194 -0
  151. data/tests/test_process_watch.rb +48 -0
  152. data/tests/test_processes.rb +128 -0
  153. data/tests/test_proxy_connection.rb +180 -0
  154. data/tests/test_pure.rb +88 -0
  155. data/tests/test_queue.rb +50 -0
  156. data/tests/test_resolver.rb +55 -0
  157. data/tests/test_running.rb +14 -0
  158. data/tests/test_sasl.rb +47 -0
  159. data/tests/test_send_file.rb +217 -0
  160. data/tests/test_servers.rb +33 -0
  161. data/tests/test_set_sock_opt.rb +37 -0
  162. data/tests/test_shutdown_hooks.rb +23 -0
  163. data/tests/test_smtpclient.rb +55 -0
  164. data/tests/test_smtpserver.rb +57 -0
  165. data/tests/test_spawn.rb +293 -0
  166. data/tests/test_ssl_args.rb +78 -0
  167. data/tests/test_ssl_echo_data.rb +60 -0
  168. data/tests/test_ssl_methods.rb +56 -0
  169. data/tests/test_ssl_verify.rb +82 -0
  170. data/tests/test_stomp.rb +37 -0
  171. data/tests/test_system.rb +42 -0
  172. data/tests/test_threaded_resource.rb +53 -0
  173. data/tests/test_tick_loop.rb +59 -0
  174. data/tests/test_timers.rb +123 -0
  175. data/tests/test_ud.rb +8 -0
  176. data/tests/test_unbind_reason.rb +48 -0
  177. metadata +297 -0
@@ -0,0 +1,521 @@
1
+ # @title Getting Started with Ruby EventMachine
2
+ # @markup markdown
3
+ # @author Michael S. Klishin, Dan Sinclair
4
+
5
+ # Getting started with Ruby EventMachine #
6
+
7
+
8
+ ## About this guide ##
9
+
10
+ This guide is a quick tutorial that helps you to get started with EventMachine for writing event-driven
11
+ servers, clients and using it as a lightweight concurrency library.
12
+ It should take about 20 minutes to read and study the provided code examples. This guide covers
13
+
14
+ * Installing EventMachine via [Rubygems](http://rubygems.org) and [Bundler](http://gembundler.com).
15
+ * Building an Echo server, the "Hello, world"-like code example of network servers.
16
+ * Building a simple chat, both server and client.
17
+ * Building a very small asynchronous Websockets client.
18
+
19
+
20
+ ## Covered versions ##
21
+
22
+ This guide covers EventMachine v0.12.10 and 1.0 (including betas).
23
+
24
+
25
+ ## Level ##
26
+
27
+ This guide assumes you are comfortable (but not necessary a guru) with the command line. On Microsoft Windows™,
28
+ we recommend you to use [JRuby](http://jruby.org) when running these examples.
29
+
30
+
31
+ ## Installing EventMachine ##
32
+
33
+ ### Make sure you have Ruby installed ###
34
+
35
+ This guide assumes you have one of the supported Ruby implementations installed:
36
+
37
+ * Ruby 1.8.7
38
+ * Ruby 1.9.2
39
+ * [JRuby](http://jruby.org) (we recommend 1.6)
40
+ * [Rubinius](http://rubini.us) 1.2 or higher
41
+ * [Ruby Enterprise Edition](http://www.rubyenterpriseedition.com)
42
+
43
+ EventMachine works on Microsoft Windows™.
44
+
45
+
46
+ ### With Rubygems ###
47
+
48
+ To install the EventMachine gem do
49
+
50
+ gem install eventmachine
51
+
52
+
53
+ ### With Bundler ###
54
+
55
+ gem "eventmachine"
56
+
57
+
58
+ ### Verifying your installation ###
59
+
60
+ Lets verify your installation with this quick IRB session:
61
+
62
+ irb -rubygems
63
+
64
+ ruby-1.9.2-p180 :001 > require "eventmachine"
65
+ => true
66
+ ruby-1.9.2-p180 :002 > EventMachine::VERSION
67
+ => "1.0.0.beta.3"
68
+
69
+
70
+ ## An Echo Server Example ##
71
+
72
+ Lets begin with the classic "Hello, world"-like example, an echo server. The echo server responds clients with the
73
+ same data that was provided. First, here's the code:
74
+
75
+ {include:file:examples/guides/getting\_started/01\_eventmachine\_echo_server.rb}
76
+
77
+
78
+ When run, the server binds to port 10000. We can connect using Telnet and verify it's working:
79
+
80
+ telnet localhost 10000
81
+
82
+ On my machine the output looks like:
83
+
84
+ ~ telnet localhost 10000
85
+ Trying 127.0.0.1...
86
+ Connected to localhost.
87
+ Escape character is '^]'.
88
+
89
+ Let's send something to our server. Type in "Hello, EventMachine" and hit Enter. The server will respond with
90
+ the same string:
91
+
92
+ ~ telnet localhost 10000
93
+ Trying 127.0.0.1...
94
+ Connected to localhost.
95
+ Escape character is '^]'.
96
+ Hello, EventMachine
97
+ # (here we hit Enter)
98
+ Hello, EventMachine
99
+ # (this ^^^ is our echo server reply)
100
+
101
+ It works! Congratulations, you now can tell your Node.js-loving friends that you "have done some event-driven programming, too".
102
+ Oh, and to stop Telnet, hit Control + Shift + ] and then Control + C.
103
+
104
+ Lets walk this example line by line and see what's going on. These lines
105
+
106
+ require 'rubygems' # or use Bundler.setup
107
+ require 'eventmachine'
108
+
109
+ probably look familiar: you use [RubyGems](http://rubygems.org) (or [Bundler](http://gembundler.com/)) for dependencies and then require EventMachine gem. Boring.
110
+
111
+ Next:
112
+
113
+ class EchoServer < EventMachine::Connection
114
+ def receive_data(data)
115
+ send_data(data)
116
+ end
117
+ end
118
+
119
+ Is the implementation of our echo server. We define a class that inherits from {EventMachine::Connection}
120
+ and a handler (aka callback) for one event: when we receive data from a client.
121
+
122
+ EventMachine handles the connection setup, receiving data and passing it to our handler, {EventMachine::Connection#receive_data}.
123
+
124
+ Then we implement our protocol logic, which in the case of Echo is pretty trivial: we send back whatever we receive.
125
+ To do so, we're using {EventMachine::Connection#send_data}.
126
+
127
+ Lets modify the example to recognize `exit` command:
128
+
129
+ {include:file:examples/guides/getting\_started/02\_eventmachine\_echo_server\_that\_recognizes\_exit\_command.rb}
130
+
131
+ Our `receive\_data` changed slightly and now looks like this:
132
+
133
+ def receive_data(data)
134
+ if data.strip =~ /exit$/i
135
+ EventMachine.stop_event_loop
136
+ else
137
+ send_data(data)
138
+ end
139
+ end
140
+
141
+ Because incoming data has trailing newline character, we strip it off before matching it against a simple regular
142
+ expression. If the data ends in `exit`, we stop EventMachine event loop with {EventMachine.stop_event_loop}. This unblocks
143
+ main thread and it finishes execution, and our little program exits as the result.
144
+
145
+ To summarize this first example:
146
+
147
+ * Subclass {EventMachine::Connection} and override {EventMachine::Connection#send_data} to handle incoming data.
148
+ * Use {EventMachine.run} to start EventMachine event loop and then bind echo server with {EventMachine.start_server}.
149
+ * To stop the event loop, use {EventMachine.stop_event_loop} (aliased as {EventMachine.stop})
150
+
151
+ Lets move on to a slightly more sophisticated example that will introduce several more features and methods
152
+ EventMachine has to offer.
153
+
154
+
155
+ ## A Simple Chat Server Example ##
156
+
157
+ Next we will write a simple chat. Initially clients will still use telnet to connect, but then we will add little
158
+ client application that will serve as a proxy between telnet and the chat server. This example is certainly longer
159
+ (~ 150 lines with whitespace and comments) so instead of looking at the final version and going through it line by line,
160
+ we will instead begin with a very simple version that only keeps track of connected clients and then add features
161
+ as we go.
162
+
163
+ To set some expectations about our example:
164
+
165
+ * It will keep track of connected clients
166
+ * It will support a couple of commands, à la IRC
167
+ * It will support direct messages using Twitter-like @usernames
168
+ * It won't use MongoDB, fibers or distributed map/reduce for anything but will be totally [Web Scale™](http://bit.ly/webscaletm) nonetheless. Maybe even [ROFLscale](http://bit.ly/roflscalevideo).
169
+
170
+ ### Step one: detecting connections and disconnectons ###
171
+
172
+ First step looks like this:
173
+
174
+ {include:file:examples/guides/getting\_started/04\_simple\_chat\_server\_step\_one.rb}
175
+
176
+ We see familiar {EventMachine.run} and {EventMachine.start_server}, but also {EventMachine::Connection#post_init} and {EventMachine::Connection#unbind} we haven't
177
+ met yet. We don't use them in this code, so when are they run? Like {EventMachine::Connection#receive_data}, these methods are callbacks. EventMachine calls them
178
+ when certain events happen:
179
+
180
+ * {EventMachine#post_init} is called by the event loop immediately after the network connection has been established.
181
+ In the chat server example case, this is when a new client connects.
182
+ * {EventMachine#unbind} is called when client disconnects, connection is closed or is lost (because of a network issue, for example).
183
+
184
+ All our chat server does so far is logging connections or disconnections. What we want it to do next is to keep track of connected clients.
185
+
186
+
187
+ ### Step two: keep track of connected clients ###
188
+
189
+ Next iteration of the code looks like this:
190
+
191
+ {include:file:examples/guides/getting\_started/05\_simple\_chat\_server\_step\_two.rb}
192
+
193
+ While the code we added is very straightforward, we have to clarify one this first: subclasses of {EventMachine::Connection} are instantiated by
194
+ EventMachine for every new connected peer. So for 10 connected chat clients, there will be 10 separate `SimpleChatServer` instances in our
195
+ server process. Like any other objects, they can be stored in a collection, can provide public API other objects use, can instantiate or inject
196
+ dependencies and in general live a happy life all Ruby objects live until garbage collection happens.
197
+
198
+ In the example above we use a @@class_variable to keep track of connected clients. In Ruby, @@class variables are accessible from instance
199
+ methods so we can add new connections to the list from `SimpleChatServer#post_init` and remove them in `SimpleChatServer#unbind`. We can also
200
+ filter connections by some criteria, as `SimpleChatServer#other_peers demonstrates`.
201
+
202
+ So, we keep track of connections but how do we identify them? For a chat app, it's pretty common to use usernames for that. Lets ask our clients
203
+ to enter usernames when they connect.
204
+
205
+
206
+ ### Step three: adding usernames ##
207
+
208
+ To add usernames, we need to add a few things:
209
+
210
+ * We need to invite newly connected clients to enter their username.
211
+ * A reader (getter) method on our {EventMachine::Connection} subclass.
212
+ * An idea of connection state (keeping track of whether a particular participant had entered username before).
213
+
214
+ Here is one way to do it:
215
+
216
+ {include:file:examples/guides/getting\_started/06\_simple\_chat\_server\_step\_three.rb}
217
+
218
+ This is quite an update so lets take a look at each method individually. First, `SimpleChatServer#post_init`:
219
+
220
+ def post_init
221
+ @username = nil
222
+ puts "A client has connected..."
223
+ ask_username
224
+ end
225
+
226
+ To keep track of username we ask chat participants for, we add @username instance variable to our connection class. Connection
227
+ instances are just Ruby objects associated with a particular connected peer, so using @ivars is very natural. To make username
228
+ value accessible to other objects, we added a reader method that was not shown on the snippet above.
229
+
230
+ Lets dig into `SimpleChatServer#ask_username`:
231
+
232
+ def ask_username
233
+ self.send_line("[info] Enter your username:")
234
+ end # ask_username
235
+
236
+ # ...
237
+
238
+ def send_line(line)
239
+ self.send_data("#{line}\n")
240
+ end # send_line(line)
241
+
242
+ Nothing new here, we are using {EventMachine::Connection#send_data} which we have seen before.
243
+
244
+
245
+ In `SimpleChatServer#receive_data` we now have to check if the username was entered or we need
246
+ to ask for it:
247
+
248
+ def receive_data(data)
249
+ if entered_username?
250
+ handle_chat_message(data.strip)
251
+ else
252
+ handle_username(data.strip)
253
+ end
254
+ end
255
+
256
+ # ...
257
+
258
+ def entered_username?
259
+ !@username.nil? && !@username.empty?
260
+ end # entered_username?
261
+
262
+ Finally, handler of chat messages is not yet implemented:
263
+
264
+ def handle_chat_message(msg)
265
+ raise NotImplementedError
266
+ end
267
+
268
+ Lets try this example out using Telnet:
269
+
270
+ ~ telnet localhost 10000
271
+ Trying 127.0.0.1...
272
+ Connected to localhost.
273
+ Escape character is '^]'.
274
+ [info] Enter your username:
275
+ antares_
276
+ [info] Ohai, antares_
277
+
278
+ and the server output:
279
+
280
+ A client has connected...
281
+ antares_ has joined
282
+
283
+ This version requires you to remember how to terminate your Telnet session (Ctrl + Shift + ], then Ctrl + C).
284
+ It is annoying, so why don't we add the same `exit` command to our chat server?
285
+
286
+
287
+ ### Step four: adding exit command and delivering chat messages ####
288
+
289
+ {include:file:examples/guides/getting\_started/07\_simple\_chat\_server\_step\_four.rb}
290
+
291
+ TBD
292
+
293
+ Lets test-drive this version. Client A:
294
+
295
+ ~ telnet localhost 10000
296
+ Trying 127.0.0.1...
297
+ Connected to localhost.
298
+ Escape character is '^]'.
299
+ [info] Enter your username:
300
+ michael
301
+ [info] Ohai, michael
302
+ Hi everyone
303
+ michael: Hi everyone
304
+ joe has joined the room
305
+ # here ^^^ client B connects, lets greet him
306
+ hi joe
307
+ michael: hi joe
308
+ joe: hey michael
309
+ # ^^^ client B replies
310
+ exit
311
+ # ^^^ out command in action
312
+ Connection closed by foreign host.
313
+
314
+ Client B:
315
+
316
+ ~ telnet localhost 10000
317
+ Trying 127.0.0.1...
318
+ Connected to localhost.
319
+ Escape character is '^]'.
320
+ [info] Enter your username:
321
+ joe
322
+ [info] Ohai, joe
323
+ michael: hi joe
324
+ # ^^^ client A greets us, lets reply
325
+ hey michael
326
+ joe: hey michael
327
+ exit
328
+ # ^^^ out command in action
329
+ Connection closed by foreign host.
330
+
331
+ And finally, the server output:
332
+
333
+ A client has connected...
334
+ michael has joined
335
+ A client has connected...
336
+ _antares has joined
337
+ [info] _antares has left
338
+ [info] michael has left
339
+
340
+ Our little char server now supports usernames, sending messages and the `exit` command. Next up, private (aka direct) messages.
341
+
342
+
343
+ ### Step five: adding direct messages and one more command ###
344
+
345
+ To add direct messages, we come up with a simple convention: private messages begin with @username and may have optional colon before
346
+ message text, like this:
347
+
348
+ @joe: hey, how do you like eventmachine?
349
+
350
+ This convention makes parsing of messages simple so that we can concentrate on delivering them to a particular client connection.
351
+ Remember when we added `username` reader on our connection class? That tiny change makes this step possible: when a new direct
352
+ message comes in, we extract username and message text and then find then connection for @username in question:
353
+
354
+ #
355
+ # Message handling
356
+ #
357
+
358
+ def handle_chat_message(msg)
359
+ if command?(msg)
360
+ self.handle_command(msg)
361
+ else
362
+ if direct_message?(msg)
363
+ self.handle_direct_message(msg)
364
+ else
365
+ self.announce(msg, "#{@username}:")
366
+ end
367
+ end
368
+ end # handle_chat_message(msg)
369
+
370
+ def direct_message?(input)
371
+ input =~ DM_REGEXP
372
+ end # direct_message?(input)
373
+
374
+ def handle_direct_message(input)
375
+ username, message = parse_direct_message(input)
376
+
377
+ if connection = @@connected_clients.find { |c| c.username == username }
378
+ puts "[dm] @#{@username} => @#{username}"
379
+ connection.send_line("[dm] @#{@username}: #{message}")
380
+ else
381
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
382
+ end
383
+ end # handle_direct_message(input)
384
+
385
+ def parse_direct_message(input)
386
+ return [$1, $2] if input =~ DM_REGEXP
387
+ end # parse_direct_message(input)
388
+
389
+ This snippet demonstrates how one connection instance can obtain another connection instance and send data to it.
390
+ This is a very powerful feature, consider just a few use cases:
391
+
392
+ * Peer-to-peer protocols
393
+ * Content-aware routing
394
+ * Efficient streaming with optional filtering
395
+
396
+ Less common use cases include extending C++ core of EventMachine to provide access to hardware that streams events that
397
+ can be re-broadcasted to any interested parties connected via TCP, UDP or something like AMQP or WebSockets. With this,
398
+ sky is the limit. Actually, EventMachine has several features for efficient proxying data between connections.
399
+ We will not cover them in this guide.
400
+
401
+ One last feature that we are going to add to our chat server is the `status` command that tells you current server time and how many people
402
+ are there in the chat room:
403
+
404
+ #
405
+ # Commands handling
406
+ #
407
+
408
+ def command?(input)
409
+ input =~ /(exit|status)$/i
410
+ end # command?(input)
411
+
412
+ def handle_command(cmd)
413
+ case cmd
414
+ when /exit$/i then self.close_connection
415
+ when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room")
416
+ end
417
+ end # handle_command(cmd)
418
+
419
+ Hopefully this piece of code is easy to follow. Try adding a few more commands, for example, the `whoishere` command that lists people
420
+ currently in the chat room.
421
+
422
+ In the end, our chat server looks like this:
423
+
424
+ {include:file:examples/guides/getting\_started/08\_simple\_chat\_server\_step\_five.rb}
425
+
426
+ We are almost done with the server but there are some closing thoughts.
427
+
428
+
429
+ ### Step six: final version ###
430
+
431
+ Just in case, here is the final version of the chat server code we have built:
432
+
433
+ {include:file:examples/guides/getting\_started/03\_simple\_chat\_server.rb}
434
+
435
+
436
+ ### Step seven: future directions and some closing thoughts ###
437
+
438
+ The chat server is just about 150 lines of Ruby including empty lines and comments, yet it has a few features most of chat server
439
+ examples never add. We did not, however, implement many other features that popular IRC clients like [Colloquy](http://colloquy.info) have:
440
+
441
+ * Chat moderation
442
+ * Multiple rooms
443
+ * Connection timeout detection
444
+
445
+ How would one go about implementing them? We thought it is worth discussing what else EventMachine has to offer and what ecosystem projects
446
+ one can use to build a really feature-rich Web-based IRC chat client.
447
+
448
+ With multiple rooms it's more or less straightforward, just add one more hash and a bunch of commands and use the information about which rooms participant
449
+ is in when you are delivering messages. There is nothing in EventMachine itself that can make the job much easier for developer.
450
+
451
+ To implement chat moderation feature you may want to do a few things:
452
+
453
+ * Work with client IP addresses. Maybe we want to consider everyone who connects from certain IPs a moderator.
454
+ * Access persistent data about usernames of moderators and their credentials.
455
+
456
+ Does EventMachine have anything to offer here? It does. To obtain peer IP address, take a look at {EventMachine::Connection#get_peername}. The name of this method is
457
+ a little bit misleading and originates from low-level socket programming APIs.
458
+
459
+ #### A whirlwind tour of the EventMachine ecosystem ####
460
+
461
+ To work with data stores you can use several database drivers that ship with EventMachine itself, however, quite often there are some 3rd party projects in
462
+ the EventMachine ecosystem that have more features, are faster or just better maintained. So we figured it will be helpful to provide a few pointers
463
+ to some of those projects:
464
+
465
+ * For MySQL, check out [em-mysql](https://github.com/eventmachine/em-mysql) project.
466
+ * For PostgreSQL, have a look at Mike Perham's [EventMachine-based PostgreSQL driver](https://github.com/mperham/em_postgresql).
467
+ * For Redis, there is a young but already popular [em-hiredis](https://github.com/mloughran/em-hiredis) library that combines EventMachine's non-blocking I/O with
468
+ extreme performance of the official Redis C client, [hiredis](https://github.com/antirez/hiredis).
469
+ * For MongoDB, see [em-mongo](https://github.com/bcg/em-mongo)
470
+ * For Cassandra, Mike Perham [added transport agnosticism feature](http://www.mikeperham.com/2010/02/09/cassandra-and-eventmachine/) to the [cassandra gem](https://rubygems.org/gems/cassandra).
471
+
472
+ [Riak](http://www.basho.com/products_riak_overview.php) and CouchDB talk HTTP so it's possible to use [em-http-request](https://github.com/igrigorik/em-http-request).
473
+ If you are aware of EventMachine-based non-blocking drivers for these databases, as well as for HBase, let us know on the [EventMachine mailing list](http://groups.google.com/group/eventmachine).
474
+ Also, EventMachine supports TLS (aka SSL) and works well on [JRuby](http://jruby.org) and Windows.
475
+
476
+ Learn more in our {file:docs/Ecosystem.md EventMachine ecosystem} and {file:docs/TLS.md TLS (aka SSL)} guides.
477
+
478
+
479
+ #### Connection loss detection ####
480
+
481
+ Finally, connection loss detection. When our chat participant closes her laptop lid, how do we know that she is no longer active? The answer is, when EventMachine
482
+ detects TCP connectin closure, it calls {EventMachine::Connection#unbind}. Version 1.0.beta3 and later also pass an optional argument to that method. The argument
483
+ indicates what error (if any) caused the connection to be closed.
484
+
485
+ Learn more in our {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery} guide.
486
+
487
+
488
+ #### What the Chat Server Example doesn't demonstrate ####
489
+
490
+ This chat server also leaves out something production quality clients and servers must take care of: buffering. We intentionally did not include any buffering in
491
+ our chat server example: it would only distract you from learning what you really came here to learn: how to use EventMachine to build blazing fast asynchronous
492
+ networking programs quickly. However, {EventMachine::Connection#receive_data} does not offer any guarantees that you will be receiving "whole messages" all the time,
493
+ largely because the underlying transport (UDP or TCP) does not offer such guarantees. Many protocols, for example, AMQP, mandate that large content chunks are
494
+ split into smaller _frames_ of certain size. This means that [amq-client](https://github.com/ruby-amqp/amq-client) library, for instance, that has EventMachine-based driver,
495
+ has to deal with figuring out when exactly we received "the whole message". To do so, it uses buffering and employs various checks to detect _frame boundaries_.
496
+ So **don't be deceived by the simplicity of this chat example**: it intentionally leaves framing out, but real world protocols usually require it.
497
+
498
+
499
+
500
+ ## A (Proxying) Chat Client Example ##
501
+
502
+ TBD
503
+
504
+
505
+ ## Wrapping up ##
506
+
507
+ This tutorial ends here. Congratulations! You have learned quite a bit about EventMachine.
508
+
509
+
510
+ ## What to read next ##
511
+
512
+ The documentation is organized as a {file:docs/DocumentationGuidesIndex.md number of guides}, covering all kinds of
513
+ topics. TBD
514
+
515
+
516
+ ## Tell us what you think! ##
517
+
518
+ Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3)
519
+ or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered?
520
+ Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is
521
+ key to making documentation better.