sidekick-client 0.1.0

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 (247) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +17 -0
  5. data/Rakefile +52 -0
  6. data/VERSION +1 -0
  7. data/lib/ext/README +1 -0
  8. data/lib/ext/bunny-0.6.0/LICENSE +20 -0
  9. data/lib/ext/bunny-0.6.0/README.rdoc +66 -0
  10. data/lib/ext/bunny-0.6.0/Rakefile +24 -0
  11. data/lib/ext/bunny-0.6.0/bunny.gemspec +65 -0
  12. data/lib/ext/bunny-0.6.0/examples/simple_08.rb +30 -0
  13. data/lib/ext/bunny-0.6.0/examples/simple_09.rb +30 -0
  14. data/lib/ext/bunny-0.6.0/examples/simple_ack_08.rb +33 -0
  15. data/lib/ext/bunny-0.6.0/examples/simple_ack_09.rb +33 -0
  16. data/lib/ext/bunny-0.6.0/examples/simple_consumer_08.rb +53 -0
  17. data/lib/ext/bunny-0.6.0/examples/simple_consumer_09.rb +53 -0
  18. data/lib/ext/bunny-0.6.0/examples/simple_fanout_08.rb +39 -0
  19. data/lib/ext/bunny-0.6.0/examples/simple_fanout_09.rb +39 -0
  20. data/lib/ext/bunny-0.6.0/examples/simple_headers_08.rb +40 -0
  21. data/lib/ext/bunny-0.6.0/examples/simple_headers_09.rb +40 -0
  22. data/lib/ext/bunny-0.6.0/examples/simple_publisher_08.rb +27 -0
  23. data/lib/ext/bunny-0.6.0/examples/simple_publisher_09.rb +27 -0
  24. data/lib/ext/bunny-0.6.0/examples/simple_topic_08.rb +59 -0
  25. data/lib/ext/bunny-0.6.0/examples/simple_topic_09.rb +59 -0
  26. data/lib/ext/bunny-0.6.0/lib/bunny/channel08.rb +39 -0
  27. data/lib/ext/bunny-0.6.0/lib/bunny/channel09.rb +39 -0
  28. data/lib/ext/bunny-0.6.0/lib/bunny/client08.rb +494 -0
  29. data/lib/ext/bunny-0.6.0/lib/bunny/client09.rb +460 -0
  30. data/lib/ext/bunny-0.6.0/lib/bunny/exchange08.rb +175 -0
  31. data/lib/ext/bunny-0.6.0/lib/bunny/exchange09.rb +177 -0
  32. data/lib/ext/bunny-0.6.0/lib/bunny/queue08.rb +389 -0
  33. data/lib/ext/bunny-0.6.0/lib/bunny/queue09.rb +395 -0
  34. data/lib/ext/bunny-0.6.0/lib/bunny/subscription08.rb +85 -0
  35. data/lib/ext/bunny-0.6.0/lib/bunny/subscription09.rb +85 -0
  36. data/lib/ext/bunny-0.6.0/lib/bunny.rb +87 -0
  37. data/lib/ext/bunny-0.6.0/lib/qrack/channel.rb +18 -0
  38. data/lib/ext/bunny-0.6.0/lib/qrack/client.rb +204 -0
  39. data/lib/ext/bunny-0.6.0/lib/qrack/protocol/protocol08.rb +132 -0
  40. data/lib/ext/bunny-0.6.0/lib/qrack/protocol/protocol09.rb +133 -0
  41. data/lib/ext/bunny-0.6.0/lib/qrack/protocol/spec08.rb +823 -0
  42. data/lib/ext/bunny-0.6.0/lib/qrack/protocol/spec09.rb +521 -0
  43. data/lib/ext/bunny-0.6.0/lib/qrack/qrack08.rb +23 -0
  44. data/lib/ext/bunny-0.6.0/lib/qrack/qrack09.rb +23 -0
  45. data/lib/ext/bunny-0.6.0/lib/qrack/queue.rb +53 -0
  46. data/lib/ext/bunny-0.6.0/lib/qrack/subscription.rb +102 -0
  47. data/lib/ext/bunny-0.6.0/lib/qrack/transport/buffer08.rb +276 -0
  48. data/lib/ext/bunny-0.6.0/lib/qrack/transport/buffer09.rb +276 -0
  49. data/lib/ext/bunny-0.6.0/lib/qrack/transport/frame08.rb +112 -0
  50. data/lib/ext/bunny-0.6.0/lib/qrack/transport/frame09.rb +94 -0
  51. data/lib/ext/bunny-0.6.0/spec/spec_08/bunny_spec.rb +65 -0
  52. data/lib/ext/bunny-0.6.0/spec/spec_08/connection_spec.rb +12 -0
  53. data/lib/ext/bunny-0.6.0/spec/spec_08/exchange_spec.rb +162 -0
  54. data/lib/ext/bunny-0.6.0/spec/spec_08/queue_spec.rb +206 -0
  55. data/lib/ext/bunny-0.6.0/spec/spec_09/bunny_spec.rb +65 -0
  56. data/lib/ext/bunny-0.6.0/spec/spec_09/connection_spec.rb +12 -0
  57. data/lib/ext/bunny-0.6.0/spec/spec_09/exchange_spec.rb +162 -0
  58. data/lib/ext/bunny-0.6.0/spec/spec_09/queue_spec.rb +205 -0
  59. data/lib/ext/eventmachine-0.12.10/.gitignore +14 -0
  60. data/lib/ext/eventmachine-0.12.10/README +82 -0
  61. data/lib/ext/eventmachine-0.12.10/Rakefile +374 -0
  62. data/lib/ext/eventmachine-0.12.10/docs/COPYING +60 -0
  63. data/lib/ext/eventmachine-0.12.10/docs/ChangeLog +211 -0
  64. data/lib/ext/eventmachine-0.12.10/docs/DEFERRABLES +133 -0
  65. data/lib/ext/eventmachine-0.12.10/docs/EPOLL +141 -0
  66. data/lib/ext/eventmachine-0.12.10/docs/GNU +281 -0
  67. data/lib/ext/eventmachine-0.12.10/docs/INSTALL +13 -0
  68. data/lib/ext/eventmachine-0.12.10/docs/KEYBOARD +38 -0
  69. data/lib/ext/eventmachine-0.12.10/docs/LEGAL +25 -0
  70. data/lib/ext/eventmachine-0.12.10/docs/LIGHTWEIGHT_CONCURRENCY +70 -0
  71. data/lib/ext/eventmachine-0.12.10/docs/PURE_RUBY +75 -0
  72. data/lib/ext/eventmachine-0.12.10/docs/RELEASE_NOTES +94 -0
  73. data/lib/ext/eventmachine-0.12.10/docs/SMTP +2 -0
  74. data/lib/ext/eventmachine-0.12.10/docs/SPAWNED_PROCESSES +89 -0
  75. data/lib/ext/eventmachine-0.12.10/docs/TODO +8 -0
  76. data/lib/ext/eventmachine-0.12.10/eventmachine.gemspec +40 -0
  77. data/lib/ext/eventmachine-0.12.10/examples/ex_channel.rb +43 -0
  78. data/lib/ext/eventmachine-0.12.10/examples/ex_queue.rb +2 -0
  79. data/lib/ext/eventmachine-0.12.10/examples/helper.rb +2 -0
  80. data/lib/ext/eventmachine-0.12.10/ext/binder.cpp +125 -0
  81. data/lib/ext/eventmachine-0.12.10/ext/binder.h +46 -0
  82. data/lib/ext/eventmachine-0.12.10/ext/cmain.cpp +827 -0
  83. data/lib/ext/eventmachine-0.12.10/ext/cplusplus.cpp +202 -0
  84. data/lib/ext/eventmachine-0.12.10/ext/ed.cpp +1893 -0
  85. data/lib/ext/eventmachine-0.12.10/ext/ed.h +424 -0
  86. data/lib/ext/eventmachine-0.12.10/ext/em.cpp +2282 -0
  87. data/lib/ext/eventmachine-0.12.10/ext/em.h +232 -0
  88. data/lib/ext/eventmachine-0.12.10/ext/emwin.cpp +300 -0
  89. data/lib/ext/eventmachine-0.12.10/ext/emwin.h +94 -0
  90. data/lib/ext/eventmachine-0.12.10/ext/epoll.cpp +26 -0
  91. data/lib/ext/eventmachine-0.12.10/ext/epoll.h +25 -0
  92. data/lib/ext/eventmachine-0.12.10/ext/eventmachine.h +122 -0
  93. data/lib/ext/eventmachine-0.12.10/ext/eventmachine_cpp.h +96 -0
  94. data/lib/ext/eventmachine-0.12.10/ext/extconf.rb +148 -0
  95. data/lib/ext/eventmachine-0.12.10/ext/fastfilereader/extconf.rb +83 -0
  96. data/lib/ext/eventmachine-0.12.10/ext/fastfilereader/mapper.cpp +214 -0
  97. data/lib/ext/eventmachine-0.12.10/ext/fastfilereader/mapper.h +59 -0
  98. data/lib/ext/eventmachine-0.12.10/ext/fastfilereader/rubymain.cpp +127 -0
  99. data/lib/ext/eventmachine-0.12.10/ext/files.cpp +94 -0
  100. data/lib/ext/eventmachine-0.12.10/ext/files.h +65 -0
  101. data/lib/ext/eventmachine-0.12.10/ext/kb.cpp +81 -0
  102. data/lib/ext/eventmachine-0.12.10/ext/page.cpp +107 -0
  103. data/lib/ext/eventmachine-0.12.10/ext/page.h +51 -0
  104. data/lib/ext/eventmachine-0.12.10/ext/pipe.cpp +349 -0
  105. data/lib/ext/eventmachine-0.12.10/ext/project.h +151 -0
  106. data/lib/ext/eventmachine-0.12.10/ext/rubymain.cpp +1166 -0
  107. data/lib/ext/eventmachine-0.12.10/ext/sigs.cpp +89 -0
  108. data/lib/ext/eventmachine-0.12.10/ext/sigs.h +32 -0
  109. data/lib/ext/eventmachine-0.12.10/ext/ssl.cpp +460 -0
  110. data/lib/ext/eventmachine-0.12.10/ext/ssl.h +94 -0
  111. data/lib/ext/eventmachine-0.12.10/java/.classpath +8 -0
  112. data/lib/ext/eventmachine-0.12.10/java/.project +17 -0
  113. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/EmReactor.java +570 -0
  114. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  115. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/EventableChannel.java +69 -0
  116. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/EventableDatagramChannel.java +189 -0
  117. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/EventableSocketChannel.java +364 -0
  118. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/application/Application.java +194 -0
  119. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/application/Connection.java +74 -0
  120. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/application/ConnectionFactory.java +37 -0
  121. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +46 -0
  122. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/application/PeriodicTimer.java +38 -0
  123. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/application/Timer.java +54 -0
  124. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/tests/ApplicationTest.java +109 -0
  125. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/tests/ConnectTest.java +148 -0
  126. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
  127. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
  128. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/tests/TestServers.java +75 -0
  129. data/lib/ext/eventmachine-0.12.10/java/src/com/rubyeventmachine/tests/TestTimers.java +90 -0
  130. data/lib/ext/eventmachine-0.12.10/lib/em/buftok.rb +138 -0
  131. data/lib/ext/eventmachine-0.12.10/lib/em/callback.rb +26 -0
  132. data/lib/ext/eventmachine-0.12.10/lib/em/channel.rb +57 -0
  133. data/lib/ext/eventmachine-0.12.10/lib/em/connection.rb +564 -0
  134. data/lib/ext/eventmachine-0.12.10/lib/em/deferrable.rb +192 -0
  135. data/lib/ext/eventmachine-0.12.10/lib/em/file_watch.rb +54 -0
  136. data/lib/ext/eventmachine-0.12.10/lib/em/future.rb +61 -0
  137. data/lib/ext/eventmachine-0.12.10/lib/em/messages.rb +66 -0
  138. data/lib/ext/eventmachine-0.12.10/lib/em/process_watch.rb +44 -0
  139. data/lib/ext/eventmachine-0.12.10/lib/em/processes.rb +119 -0
  140. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/header_and_content.rb +138 -0
  141. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/httpclient.rb +263 -0
  142. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/httpclient2.rb +590 -0
  143. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/line_and_text.rb +125 -0
  144. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/linetext2.rb +161 -0
  145. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/memcache.rb +323 -0
  146. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/object_protocol.rb +45 -0
  147. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/postgres3.rb +247 -0
  148. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/saslauth.rb +175 -0
  149. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/smtpclient.rb +357 -0
  150. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/smtpserver.rb +547 -0
  151. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/socks4.rb +66 -0
  152. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/stomp.rb +200 -0
  153. data/lib/ext/eventmachine-0.12.10/lib/em/protocols/tcptest.rb +53 -0
  154. data/lib/ext/eventmachine-0.12.10/lib/em/protocols.rb +36 -0
  155. data/lib/ext/eventmachine-0.12.10/lib/em/queue.rb +61 -0
  156. data/lib/ext/eventmachine-0.12.10/lib/em/spawnable.rb +85 -0
  157. data/lib/ext/eventmachine-0.12.10/lib/em/streamer.rb +130 -0
  158. data/lib/ext/eventmachine-0.12.10/lib/em/timers.rb +56 -0
  159. data/lib/ext/eventmachine-0.12.10/lib/em/version.rb +3 -0
  160. data/lib/ext/eventmachine-0.12.10/lib/eventmachine.rb +1592 -0
  161. data/lib/ext/eventmachine-0.12.10/lib/evma/callback.rb +32 -0
  162. data/lib/ext/eventmachine-0.12.10/lib/evma/container.rb +75 -0
  163. data/lib/ext/eventmachine-0.12.10/lib/evma/factory.rb +77 -0
  164. data/lib/ext/eventmachine-0.12.10/lib/evma/protocol.rb +87 -0
  165. data/lib/ext/eventmachine-0.12.10/lib/evma/reactor.rb +48 -0
  166. data/lib/ext/eventmachine-0.12.10/lib/evma.rb +32 -0
  167. data/lib/ext/eventmachine-0.12.10/lib/jeventmachine.rb +257 -0
  168. data/lib/ext/eventmachine-0.12.10/lib/pr_eventmachine.rb +1022 -0
  169. data/lib/ext/eventmachine-0.12.10/setup.rb +1585 -0
  170. data/lib/ext/eventmachine-0.12.10/tasks/cpp.rake_example +77 -0
  171. data/lib/ext/eventmachine-0.12.10/tests/client.crt +31 -0
  172. data/lib/ext/eventmachine-0.12.10/tests/client.key +51 -0
  173. data/lib/ext/eventmachine-0.12.10/tests/test_attach.rb +126 -0
  174. data/lib/ext/eventmachine-0.12.10/tests/test_basic.rb +284 -0
  175. data/lib/ext/eventmachine-0.12.10/tests/test_channel.rb +63 -0
  176. data/lib/ext/eventmachine-0.12.10/tests/test_connection_count.rb +35 -0
  177. data/lib/ext/eventmachine-0.12.10/tests/test_defer.rb +47 -0
  178. data/lib/ext/eventmachine-0.12.10/tests/test_epoll.rb +160 -0
  179. data/lib/ext/eventmachine-0.12.10/tests/test_error_handler.rb +35 -0
  180. data/lib/ext/eventmachine-0.12.10/tests/test_errors.rb +82 -0
  181. data/lib/ext/eventmachine-0.12.10/tests/test_exc.rb +55 -0
  182. data/lib/ext/eventmachine-0.12.10/tests/test_file_watch.rb +49 -0
  183. data/lib/ext/eventmachine-0.12.10/tests/test_futures.rb +198 -0
  184. data/lib/ext/eventmachine-0.12.10/tests/test_get_sock_opt.rb +30 -0
  185. data/lib/ext/eventmachine-0.12.10/tests/test_handler_check.rb +37 -0
  186. data/lib/ext/eventmachine-0.12.10/tests/test_hc.rb +218 -0
  187. data/lib/ext/eventmachine-0.12.10/tests/test_httpclient.rb +218 -0
  188. data/lib/ext/eventmachine-0.12.10/tests/test_httpclient2.rb +153 -0
  189. data/lib/ext/eventmachine-0.12.10/tests/test_inactivity_timeout.rb +50 -0
  190. data/lib/ext/eventmachine-0.12.10/tests/test_kb.rb +60 -0
  191. data/lib/ext/eventmachine-0.12.10/tests/test_ltp.rb +182 -0
  192. data/lib/ext/eventmachine-0.12.10/tests/test_ltp2.rb +317 -0
  193. data/lib/ext/eventmachine-0.12.10/tests/test_next_tick.rb +133 -0
  194. data/lib/ext/eventmachine-0.12.10/tests/test_object_protocol.rb +37 -0
  195. data/lib/ext/eventmachine-0.12.10/tests/test_pause.rb +70 -0
  196. data/lib/ext/eventmachine-0.12.10/tests/test_pending_connect_timeout.rb +48 -0
  197. data/lib/ext/eventmachine-0.12.10/tests/test_process_watch.rb +48 -0
  198. data/lib/ext/eventmachine-0.12.10/tests/test_processes.rb +128 -0
  199. data/lib/ext/eventmachine-0.12.10/tests/test_proxy_connection.rb +92 -0
  200. data/lib/ext/eventmachine-0.12.10/tests/test_pure.rb +125 -0
  201. data/lib/ext/eventmachine-0.12.10/tests/test_queue.rb +44 -0
  202. data/lib/ext/eventmachine-0.12.10/tests/test_running.rb +42 -0
  203. data/lib/ext/eventmachine-0.12.10/tests/test_sasl.rb +72 -0
  204. data/lib/ext/eventmachine-0.12.10/tests/test_send_file.rb +242 -0
  205. data/lib/ext/eventmachine-0.12.10/tests/test_servers.rb +76 -0
  206. data/lib/ext/eventmachine-0.12.10/tests/test_smtpclient.rb +83 -0
  207. data/lib/ext/eventmachine-0.12.10/tests/test_smtpserver.rb +85 -0
  208. data/lib/ext/eventmachine-0.12.10/tests/test_spawn.rb +322 -0
  209. data/lib/ext/eventmachine-0.12.10/tests/test_ssl_args.rb +79 -0
  210. data/lib/ext/eventmachine-0.12.10/tests/test_ssl_methods.rb +50 -0
  211. data/lib/ext/eventmachine-0.12.10/tests/test_ssl_verify.rb +82 -0
  212. data/lib/ext/eventmachine-0.12.10/tests/test_timers.rb +162 -0
  213. data/lib/ext/eventmachine-0.12.10/tests/test_ud.rb +36 -0
  214. data/lib/ext/eventmachine-0.12.10/tests/testem.rb +31 -0
  215. data/lib/ext/eventmachine-0.12.10/web/whatis +7 -0
  216. data/lib/ext/misc/README +1 -0
  217. data/lib/ext/misc/indifferent_access.rb +131 -0
  218. data/lib/ext/uuidtools-2.1.1/CHANGELOG +56 -0
  219. data/lib/ext/uuidtools-2.1.1/LICENSE +20 -0
  220. data/lib/ext/uuidtools-2.1.1/README +13 -0
  221. data/lib/ext/uuidtools-2.1.1/Rakefile +48 -0
  222. data/lib/ext/uuidtools-2.1.1/lib/compat/securerandom.rb +202 -0
  223. data/lib/ext/uuidtools-2.1.1/lib/uuidtools/version.rb +35 -0
  224. data/lib/ext/uuidtools-2.1.1/lib/uuidtools.rb +618 -0
  225. data/lib/ext/uuidtools-2.1.1/spec/spec.opts +1 -0
  226. data/lib/ext/uuidtools-2.1.1/spec/spec_helper.rb +7 -0
  227. data/lib/ext/uuidtools-2.1.1/spec/uuidtools/mac_address_spec.rb +15 -0
  228. data/lib/ext/uuidtools-2.1.1/spec/uuidtools/utility_spec.rb +21 -0
  229. data/lib/ext/uuidtools-2.1.1/spec/uuidtools/uuid_creation_spec.rb +121 -0
  230. data/lib/ext/uuidtools-2.1.1/spec/uuidtools/uuid_parsing_spec.rb +127 -0
  231. data/lib/ext/uuidtools-2.1.1/tasks/benchmark.rake +38 -0
  232. data/lib/ext/uuidtools-2.1.1/tasks/clobber.rake +2 -0
  233. data/lib/ext/uuidtools-2.1.1/tasks/gem.rake +68 -0
  234. data/lib/ext/uuidtools-2.1.1/tasks/git.rake +40 -0
  235. data/lib/ext/uuidtools-2.1.1/tasks/metrics.rake +22 -0
  236. data/lib/ext/uuidtools-2.1.1/tasks/rdoc.rake +29 -0
  237. data/lib/ext/uuidtools-2.1.1/tasks/rubyforge.rake +89 -0
  238. data/lib/ext/uuidtools-2.1.1/tasks/spec.rake +64 -0
  239. data/lib/ext/uuidtools-2.1.1/website/index.html +95 -0
  240. data/lib/sidekick/client/sidekick_client.rb +46 -0
  241. data/lib/sidekick/shared/sidekick_logger.rb +14 -0
  242. data/lib/sidekick/shared/sidekick_queue_publisher.rb +91 -0
  243. data/lib/sidekick-client.rb +7 -0
  244. data/sidekick-client.gemspec +289 -0
  245. data/test/helper.rb +10 -0
  246. data/test/test_sidekick-client.rb +7 -0
  247. metadata +311 -0
@@ -0,0 +1,138 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 Nov 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
+
29
+ # === Usage
30
+ #
31
+ # class RequestHandler < EM::P::HeaderAndContentProtocol
32
+ # def receive_request headers, content
33
+ # p [:request, headers, content]
34
+ # end
35
+ # end
36
+ #
37
+ # EM.run{
38
+ # EM.start_server 'localhost', 80, RequestHandler
39
+ # }
40
+ #
41
+ #--
42
+ # Originally, this subclassed LineAndTextProtocol, which in
43
+ # turn relies on BufferedTokenizer, which doesn't gracefully
44
+ # handle the transitions between lines and binary text.
45
+ # Changed 13Sep08 by FCianfrocca.
46
+ class HeaderAndContentProtocol < Connection
47
+ include LineText2
48
+
49
+ ContentLengthPattern = /Content-length:\s*(\d+)/i
50
+
51
+ def initialize *args
52
+ super
53
+ init_for_request
54
+ end
55
+
56
+ def receive_line line
57
+ case @hc_mode
58
+ when :discard_blanks
59
+ unless line == ""
60
+ @hc_mode = :headers
61
+ receive_line line
62
+ end
63
+ when :headers
64
+ if line == ""
65
+ raise "unrecognized state" unless @hc_headers.length > 0
66
+ if respond_to?(:receive_headers)
67
+ receive_headers @hc_headers
68
+ end
69
+ # @hc_content_length will be nil, not 0, if there was no content-length header.
70
+ if @hc_content_length.to_i > 0
71
+ set_binary_mode @hc_content_length
72
+ else
73
+ dispatch_request
74
+ end
75
+ else
76
+ @hc_headers << line
77
+ if ContentLengthPattern =~ line
78
+ # There are some attacks that rely on sending multiple content-length
79
+ # headers. This is a crude protection, but needs to become tunable.
80
+ raise "extraneous content-length header" if @hc_content_length
81
+ @hc_content_length = $1.to_i
82
+ end
83
+ if @hc_headers.length == 1 and respond_to?(:receive_first_header_line)
84
+ receive_first_header_line line
85
+ end
86
+ end
87
+ else
88
+ raise "internal error, unsupported mode"
89
+ end
90
+ end
91
+
92
+ def receive_binary_data text
93
+ @hc_content = text
94
+ dispatch_request
95
+ end
96
+
97
+ def dispatch_request
98
+ if respond_to?(:receive_request)
99
+ receive_request @hc_headers, @hc_content
100
+ end
101
+ init_for_request
102
+ end
103
+ private :dispatch_request
104
+
105
+ def init_for_request
106
+ @hc_mode = :discard_blanks
107
+ @hc_headers = []
108
+ # originally was @hc_headers ||= []; @hc_headers.clear to get a performance
109
+ # boost, but it's counterproductive because a subclassed handler will have to
110
+ # call dup to use the header array we pass in receive_headers.
111
+
112
+ @hc_content_length = nil
113
+ @hc_content = ""
114
+ end
115
+ private :init_for_request
116
+
117
+ # Basically a convenience method. We might create a subclass that does this
118
+ # automatically. But it's such a performance killer.
119
+ def headers_2_hash hdrs
120
+ self.class.headers_2_hash hdrs
121
+ end
122
+
123
+ class << self
124
+ def headers_2_hash hdrs
125
+ hash = {}
126
+ hdrs.each {|h|
127
+ if /\A([^\s:]+)\s*:\s*/ =~ h
128
+ tail = $'.dup
129
+ hash[ $1.downcase.gsub(/-/,"_").intern ] = tail
130
+ end
131
+ }
132
+ hash
133
+ end
134
+ end
135
+
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,263 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+
27
+
28
+ module EventMachine
29
+ module Protocols
30
+
31
+ # === Usage
32
+ #
33
+ # EventMachine.run {
34
+ # http = EventMachine::Protocols::HttpClient.request(
35
+ # :host => server,
36
+ # :port => 80,
37
+ # :request => "/index.html",
38
+ # :query_string => "parm1=value1&parm2=value2"
39
+ # )
40
+ # http.callback {|response|
41
+ # puts response[:status]
42
+ # puts response[:headers]
43
+ # puts response[:content]
44
+ # }
45
+ # }
46
+ #--
47
+ # TODO:
48
+ # Add streaming so we can support enormous POSTs. Current max is 20meg.
49
+ # Timeout for connections that run too long or hang somewhere in the middle.
50
+ # Persistent connections (HTTP/1.1), may need a associated delegate object.
51
+ # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
52
+ # DNS lookups are unbelievably slow.
53
+ # HEAD requests.
54
+ # Chunked transfer encoding.
55
+ # Convenience methods for requests. get, post, url, etc.
56
+ # SSL.
57
+ # Handle status codes like 304, 100, etc.
58
+ # Refactor this code so that protocol errors all get handled one way (an exception?),
59
+ # instead of sprinkling set_deferred_status :failed calls everywhere.
60
+ class HttpClient < Connection
61
+ include EventMachine::Deferrable
62
+
63
+ MaxPostContentLength = 20 * 1024 * 1024
64
+
65
+ # === Arg list
66
+ # :host => 'ip/dns', :port => fixnum, :verb => 'GET', :request => 'path',
67
+ # :basic_auth => {:username => '', :password => ''}, :content => 'content',
68
+ # :contenttype => 'text/plain', :query_string => '', :host_header => '',
69
+ # :cookie => ''
70
+ def self.request( args = {} )
71
+ args[:port] ||= 80
72
+ EventMachine.connect( args[:host], args[:port], self ) {|c|
73
+ # According to the docs, we will get here AFTER post_init is called.
74
+ c.instance_eval {@args = args}
75
+ }
76
+ end
77
+
78
+ def post_init
79
+ @start_time = Time.now
80
+ @data = ""
81
+ @read_state = :base
82
+ end
83
+
84
+ # We send the request when we get a connection.
85
+ # AND, we set an instance variable to indicate we passed through here.
86
+ # That allows #unbind to know whether there was a successful connection.
87
+ # NB: This naive technique won't work when we have to support multiple
88
+ # requests on a single connection.
89
+ def connection_completed
90
+ @connected = true
91
+ send_request @args
92
+ end
93
+
94
+ def send_request args
95
+ args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
96
+ args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
97
+
98
+ verb = args[:verb].to_s.upcase
99
+ unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
100
+ set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
101
+ return # NOTE THE EARLY RETURN, we're not sending any data.
102
+ end
103
+
104
+ request = args[:request] || "/"
105
+ unless request[0,1] == "/"
106
+ request = "/" + request
107
+ end
108
+
109
+ qs = args[:query_string] || ""
110
+ if qs.length > 0 and qs[0,1] != '?'
111
+ qs = "?" + qs
112
+ end
113
+
114
+ version = args[:version] || "1.1"
115
+
116
+ # Allow an override for the host header if it's not the connect-string.
117
+ host = args[:host_header] || args[:host] || "_"
118
+ # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
119
+ port = args[:port]
120
+
121
+ # POST items.
122
+ postcontenttype = args[:contenttype] || "application/octet-stream"
123
+ postcontent = args[:content] || ""
124
+ raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
125
+
126
+ # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
127
+ # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
128
+ req = [
129
+ "#{verb} #{request}#{qs} HTTP/#{version}",
130
+ "Host: #{host}:#{port}",
131
+ "User-agent: Ruby EventMachine",
132
+ ]
133
+
134
+ if verb == "POST" || verb == "PUT"
135
+ req << "Content-type: #{postcontenttype}"
136
+ req << "Content-length: #{postcontent.length}"
137
+ end
138
+
139
+ # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
140
+ # Eventually we will want to deal intelligently with arrays and hashes.
141
+ if args[:cookie]
142
+ req << "Cookie: #{args[:cookie]}"
143
+ end
144
+
145
+ # Basic-auth stanza contributed by Matt Murphy.
146
+ if args[:basic_auth]
147
+ basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
148
+ req << "Authorization: Basic #{basic_auth_string}"
149
+ end
150
+
151
+ req << ""
152
+ reqstring = req.map {|l| "#{l}\r\n"}.join
153
+ send_data reqstring
154
+
155
+ if verb == "POST" || verb == "PUT"
156
+ send_data postcontent
157
+ end
158
+ end
159
+
160
+
161
+ def receive_data data
162
+ while data and data.length > 0
163
+ case @read_state
164
+ when :base
165
+ # Perform any per-request initialization here and don't consume any data.
166
+ @data = ""
167
+ @headers = []
168
+ @content_length = nil # not zero
169
+ @content = ""
170
+ @status = nil
171
+ @read_state = :header
172
+ @connection_close = nil
173
+ when :header
174
+ ary = data.split( /\r?\n/m, 2 )
175
+ if ary.length == 2
176
+ data = ary.last
177
+ if ary.first == ""
178
+ if (@content_length and @content_length > 0) || @connection_close
179
+ @read_state = :content
180
+ else
181
+ dispatch_response
182
+ @read_state = :base
183
+ end
184
+ else
185
+ @headers << ary.first
186
+ if @headers.length == 1
187
+ parse_response_line
188
+ elsif ary.first =~ /\Acontent-length:\s*/i
189
+ # Only take the FIRST content-length header that appears,
190
+ # which we can distinguish because @content_length is nil.
191
+ # TODO, it's actually a fatal error if there is more than one
192
+ # content-length header, because the caller is presumptively
193
+ # a bad guy. (There is an exploit that depends on multiple
194
+ # content-length headers.)
195
+ @content_length ||= $'.to_i
196
+ elsif ary.first =~ /\Aconnection:\s*close/i
197
+ @connection_close = true
198
+ end
199
+ end
200
+ else
201
+ @data << data
202
+ data = ""
203
+ end
204
+ when :content
205
+ # If there was no content-length header, we have to wait until the connection
206
+ # closes. Everything we get until that point is content.
207
+ # TODO: Must impose a content-size limit, and also must implement chunking.
208
+ # Also, must support either temporary files for large content, or calling
209
+ # a content-consumer block supplied by the user.
210
+ if @content_length
211
+ bytes_needed = @content_length - @content.length
212
+ @content += data[0, bytes_needed]
213
+ data = data[bytes_needed..-1] || ""
214
+ if @content_length == @content.length
215
+ dispatch_response
216
+ @read_state = :base
217
+ end
218
+ else
219
+ @content << data
220
+ data = ""
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+
227
+ # We get called here when we have received an HTTP response line.
228
+ # It's an opportunity to throw an exception or trigger other exceptional
229
+ # handling.
230
+ def parse_response_line
231
+ if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
232
+ @status = $1.to_i
233
+ else
234
+ set_deferred_status :failed, {
235
+ :status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
236
+ }
237
+ close_connection
238
+ end
239
+ end
240
+ private :parse_response_line
241
+
242
+ def dispatch_response
243
+ @read_state = :base
244
+ set_deferred_status :succeeded, {
245
+ :content => @content,
246
+ :headers => @headers,
247
+ :status => @status
248
+ }
249
+ # TODO, we close the connection for now, but this is wrong for persistent clients.
250
+ close_connection
251
+ end
252
+
253
+ def unbind
254
+ if !@connected
255
+ set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
256
+ elsif (@read_state == :content and @content_length == nil)
257
+ dispatch_response
258
+ end
259
+ end
260
+ end
261
+
262
+ end
263
+ end
@@ -0,0 +1,590 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+ module EventMachine
27
+ module Protocols
28
+
29
+ # === Usage
30
+ #
31
+ # EM.run{
32
+ # conn = EM::Protocols::HttpClient2.connect 'google.com', 80
33
+ #
34
+ # req = conn.get('/')
35
+ # req.callback{ |response|
36
+ # p(response.status)
37
+ # p(response.headers)
38
+ # p(response.content)
39
+ # }
40
+ # }
41
+ class HttpClient2 < Connection
42
+ include LineText2
43
+
44
+ def initialize
45
+ @authorization = nil
46
+ @closed = nil
47
+ @requests = nil
48
+ end
49
+
50
+ class Request # :nodoc:
51
+ include Deferrable
52
+
53
+ attr_reader :version
54
+ attr_reader :status
55
+ attr_reader :header_lines
56
+ attr_reader :headers
57
+ attr_reader :content
58
+ attr_reader :internal_error
59
+
60
+ def initialize conn, args
61
+ @conn = conn
62
+ @args = args
63
+ @header_lines = []
64
+ @headers = {}
65
+ @blanks = 0
66
+ @chunk_trailer = nil
67
+ @chunking = nil
68
+ end
69
+
70
+ def send_request
71
+ az = @args[:authorization] and az = "Authorization: #{az}\r\n"
72
+
73
+ r = [
74
+ "#{@args[:verb]} #{@args[:uri]} HTTP/#{@args[:version] || "1.1"}\r\n",
75
+ "Host: #{@args[:host_header] || "_"}\r\n",
76
+ az || "",
77
+ "\r\n"
78
+ ]
79
+ @conn.send_data r.join
80
+ end
81
+
82
+
83
+ #--
84
+ #
85
+ def receive_line ln
86
+ if @chunk_trailer
87
+ receive_chunk_trailer(ln)
88
+ elsif @chunking
89
+ receive_chunk_header(ln)
90
+ else
91
+ receive_header_line(ln)
92
+ end
93
+ end
94
+
95
+ #--
96
+ #
97
+ def receive_chunk_trailer ln
98
+ if ln.length == 0
99
+ @conn.pop_request
100
+ succeed(self)
101
+ else
102
+ p "Received chunk trailer line"
103
+ end
104
+ end
105
+
106
+ #--
107
+ # Allow up to ten blank lines before we get a real response line.
108
+ # Allow no more than 100 lines in the header.
109
+ #
110
+ def receive_header_line ln
111
+ if ln.length == 0
112
+ if @header_lines.length > 0
113
+ process_header
114
+ else
115
+ @blanks += 1
116
+ if @blanks > 10
117
+ @conn.close_connection
118
+ end
119
+ end
120
+ else
121
+ @header_lines << ln
122
+ if @header_lines.length > 100
123
+ @internal_error = :bad_header
124
+ @conn.close_connection
125
+ end
126
+ end
127
+ end
128
+
129
+ #--
130
+ # Cf RFC 2616 pgh 3.6.1 for the format of HTTP chunks.
131
+ #
132
+ def receive_chunk_header ln
133
+ if ln.length > 0
134
+ chunksize = ln.to_i(16)
135
+ if chunksize > 0
136
+ @conn.set_text_mode(ln.to_i(16))
137
+ else
138
+ @content = @content ? @content.join : ''
139
+ @chunk_trailer = true
140
+ end
141
+ else
142
+ # We correctly come here after each chunk gets read.
143
+ # p "Got A BLANK chunk line"
144
+ end
145
+
146
+ end
147
+
148
+
149
+ #--
150
+ # We get a single chunk. Append it to the incoming content and switch back to line mode.
151
+ #
152
+ def receive_chunked_text text
153
+ # p "RECEIVED #{text.length} CHUNK"
154
+ (@content ||= []) << text
155
+ end
156
+
157
+
158
+ #--
159
+ # TODO, inefficient how we're handling this. Part of it is done so as to
160
+ # make sure we don't have problems in detecting chunked-encoding, content-length,
161
+ # etc.
162
+ #
163
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
164
+ ClenRE = /\AContent-length:\s*(\d+)/i
165
+ ChunkedRE = /\ATransfer-encoding:\s*chunked/i
166
+ ColonRE = /\:\s*/
167
+
168
+ def process_header
169
+ unless @header_lines.first =~ HttpResponseRE
170
+ @conn.close_connection
171
+ @internal_error = :bad_request
172
+ end
173
+ @version = $1.dup
174
+ @status = $2.dup.to_i
175
+
176
+ clen = nil
177
+ chunks = nil
178
+ @header_lines.each_with_index do |e,ix|
179
+ if ix > 0
180
+ hdr,val = e.split(ColonRE,2)
181
+ (@headers[hdr.downcase] ||= []) << val
182
+ end
183
+
184
+ if clen == nil and e =~ ClenRE
185
+ clen = $1.dup.to_i
186
+ end
187
+ if e =~ ChunkedRE
188
+ chunks = true
189
+ end
190
+ end
191
+
192
+ if clen
193
+ # If the content length is zero we should not call set_text_mode,
194
+ # because a value of zero will make it wait forever, hanging the
195
+ # connection. Just return success instead, with empty content.
196
+ if clen == 0 then
197
+ @content = ""
198
+ @conn.pop_request
199
+ succeed(self)
200
+ else
201
+ @conn.set_text_mode clen
202
+ end
203
+ elsif chunks
204
+ @chunking = true
205
+ else
206
+ # Chunked transfer, multipart, or end-of-connection.
207
+ # For end-of-connection, we need to go the unbind
208
+ # method and suppress its desire to fail us.
209
+ p "NO CLEN"
210
+ p @args[:uri]
211
+ p @header_lines
212
+ @internal_error = :unsupported_clen
213
+ @conn.close_connection
214
+ end
215
+ end
216
+ private :process_header
217
+
218
+
219
+ def receive_text text
220
+ @chunking ? receive_chunked_text(text) : receive_sized_text(text)
221
+ end
222
+
223
+ #--
224
+ # At the present time, we only handle contents that have a length
225
+ # specified by the content-length header.
226
+ #
227
+ def receive_sized_text text
228
+ @content = text
229
+ @conn.pop_request
230
+ succeed(self)
231
+ end
232
+ end
233
+
234
+ # Make a connection to a remote HTTP server.
235
+ # Can take either a pair of arguments (which will be interpreted as
236
+ # a hostname/ip-address and a port), or a hash.
237
+ # If the arguments are a hash, then supported values include:
238
+ # :host => a hostname or ip-address
239
+ # :port => a port number
240
+ # :ssl => true to enable ssl
241
+ def self.connect *args
242
+ if args.length == 2
243
+ args = {:host=>args[0], :port=>args[1]}
244
+ else
245
+ args = args.first
246
+ end
247
+
248
+ h,prt,ssl = args[:host], Integer(args[:port]), (args[:tls] || args[:ssl])
249
+ conn = EM.connect( h, prt, self )
250
+ conn.start_tls if ssl
251
+ conn.set_default_host_header( h, prt, ssl )
252
+ conn
253
+ end
254
+
255
+ # Get a url
256
+ #
257
+ # req = conn.get(:uri => '/')
258
+ # req.callback{|response| puts response.content }
259
+ #
260
+ def get args
261
+ if args.is_a?(String)
262
+ args = {:uri=>args}
263
+ end
264
+ args[:verb] = "GET"
265
+ request args
266
+ end
267
+
268
+ # Post to a url
269
+ #
270
+ # req = conn.post('/data')
271
+ # req.callback{|response| puts response.content }
272
+ #--
273
+ # XXX there's no way to supply a POST body.. wtf?
274
+ def post args
275
+ if args.is_a?(String)
276
+ args = {:uri=>args}
277
+ end
278
+ args[:verb] = "POST"
279
+ request args
280
+ end
281
+
282
+ # :stopdoc:
283
+
284
+ #--
285
+ # Compute and remember a string to be used as the host header in HTTP requests
286
+ # unless the user overrides it with an argument to #request.
287
+ #
288
+ def set_default_host_header host, port, ssl
289
+ if (ssl and port != 443) or (!ssl and port != 80)
290
+ @host_header = "#{host}:#{port}"
291
+ else
292
+ @host_header = host
293
+ end
294
+ end
295
+
296
+
297
+ def post_init
298
+ super
299
+ @connected = EM::DefaultDeferrable.new
300
+ end
301
+
302
+ def connection_completed
303
+ super
304
+ @connected.succeed
305
+ end
306
+
307
+ #--
308
+ # All pending requests, if any, must fail.
309
+ # We might come here without ever passing through connection_completed
310
+ # in case we can't connect to the server. We'll also get here when the
311
+ # connection closes (either because the server closes it, or we close it
312
+ # due to detecting an internal error or security violation).
313
+ # In either case, run down all pending requests, if any, and signal failure
314
+ # on them.
315
+ #
316
+ # Set and remember a flag (@closed) so we can immediately fail any
317
+ # subsequent requests.
318
+ #
319
+ def unbind
320
+ super
321
+ @closed = true
322
+ (@requests || []).each {|r| r.fail}
323
+ end
324
+
325
+ def request args
326
+ args[:host_header] = @host_header unless args.has_key?(:host_header)
327
+ args[:authorization] = @authorization unless args.has_key?(:authorization)
328
+ r = Request.new self, args
329
+ if @closed
330
+ r.fail
331
+ else
332
+ (@requests ||= []).unshift r
333
+ @connected.callback {r.send_request}
334
+ end
335
+ r
336
+ end
337
+
338
+ def receive_line ln
339
+ if req = @requests.last
340
+ req.receive_line ln
341
+ else
342
+ p "??????????"
343
+ p ln
344
+ end
345
+
346
+ end
347
+ def receive_binary_data text
348
+ @requests.last.receive_text text
349
+ end
350
+
351
+ #--
352
+ # Called by a Request object when it completes.
353
+ #
354
+ def pop_request
355
+ @requests.pop
356
+ end
357
+
358
+ # :startdoc:
359
+ end
360
+
361
+
362
+ =begin
363
+ class HttpClient2x < Connection
364
+ include LineText2
365
+
366
+ # TODO: Make this behave appropriate in case a #connect fails.
367
+ # Currently, this produces no errors.
368
+
369
+ # Make a connection to a remote HTTP server.
370
+ # Can take either a pair of arguments (which will be interpreted as
371
+ # a hostname/ip-address and a port), or a hash.
372
+ # If the arguments are a hash, then supported values include:
373
+ # :host => a hostname or ip-address;
374
+ # :port => a port number
375
+ #--
376
+ # TODO, support optional encryption arguments like :ssl
377
+ def self.connect *args
378
+ if args.length == 2
379
+ args = {:host=>args[0], :port=>args[1]}
380
+ else
381
+ args = args.first
382
+ end
383
+
384
+ h,prt = args[:host],Integer(args[:port])
385
+ EM.connect( h, prt, self, h, prt )
386
+ end
387
+
388
+
389
+ #--
390
+ # Sugars a connection that makes a single request and then
391
+ # closes the connection. Matches the behavior and the arguments
392
+ # of the original implementation of class HttpClient.
393
+ #
394
+ # Intended primarily for back compatibility, but the idiom
395
+ # is probably useful so it's not deprecated.
396
+ # We return a Deferrable, as did the original implementation.
397
+ #
398
+ # Because we're improving the way we deal with errors and exceptions
399
+ # (specifically, HTTP response codes other than 2xx will trigger the
400
+ # errback rather than the callback), this may break some existing code.
401
+ #
402
+ def self.request args
403
+ c = connect args
404
+ end
405
+
406
+ #--
407
+ # Requests can be pipelined. When we get a request, add it to the
408
+ # front of a queue as an array. The last element of the @requests
409
+ # array is always the oldest request received. Each element of the
410
+ # @requests array is a two-element array consisting of a hash with
411
+ # the original caller's arguments, and an initially-empty Ostruct
412
+ # containing the data we retrieve from the server's response.
413
+ # Maintain the instance variable @current_response, which is the response
414
+ # of the oldest pending request. That's just to make other code a little
415
+ # easier. If the variable doesn't exist when we come here, we're
416
+ # obviously the first request being made on the connection.
417
+ #
418
+ # The reason for keeping this method private (and requiring use of the
419
+ # convenience methods #get, #post, #head, etc) is to avoid the small
420
+ # performance penalty of canonicalizing the verb.
421
+ #
422
+ def request args
423
+ d = EventMachine::DefaultDeferrable.new
424
+
425
+ if @closed
426
+ d.fail
427
+ return d
428
+ end
429
+
430
+ o = OpenStruct.new
431
+ o.deferrable = d
432
+ (@requests ||= []).unshift [args, o]
433
+ @current_response ||= @requests.last.last
434
+ @connected.callback {
435
+ az = args[:authorization] and az = "Authorization: #{az}\r\n"
436
+
437
+ r = [
438
+ "#{args[:verb]} #{args[:uri]} HTTP/#{args[:version] || "1.1"}\r\n",
439
+ "Host: #{args[:host_header] || @host_header}\r\n",
440
+ az || "",
441
+ "\r\n"
442
+ ]
443
+ p r
444
+ send_data r.join
445
+ }
446
+ o.deferrable
447
+ end
448
+ private :request
449
+
450
+ def get args
451
+ if args.is_a?(String)
452
+ args = {:uri=>args}
453
+ end
454
+ args[:verb] = "GET"
455
+ request args
456
+ end
457
+
458
+ def initialize host, port
459
+ super
460
+ @host_header = "#{host}:#{port}"
461
+ end
462
+ def post_init
463
+ super
464
+ @connected = EM::DefaultDeferrable.new
465
+ end
466
+
467
+
468
+ def connection_completed
469
+ super
470
+ @connected.succeed
471
+ end
472
+
473
+ #--
474
+ # Make sure to throw away any leftover incoming data if we've
475
+ # been closed due to recognizing an error.
476
+ #
477
+ # Generate an internal error if we get an unreasonable number of
478
+ # header lines. It could be malicious.
479
+ #
480
+ def receive_line ln
481
+ p ln
482
+ return if @closed
483
+
484
+ if ln.length > 0
485
+ (@current_response.headers ||= []).push ln
486
+ abort_connection if @current_response.headers.length > 100
487
+ else
488
+ process_received_headers
489
+ end
490
+ end
491
+
492
+ #--
493
+ # We come here when we've seen all the headers for a particular request.
494
+ # What we do next depends on the response line (which should be the
495
+ # first line in the header set), and whether there is content to read.
496
+ # We may transition into a text-reading state to read content, or
497
+ # we may abort the connection, or we may go right back into parsing
498
+ # responses for the next response in the chain.
499
+ #
500
+ # We make an ASSUMPTION that the first line is an HTTP response.
501
+ # Anything else produces an error that aborts the connection.
502
+ # This may not be enough, because it may be that responses to pipelined
503
+ # requests will come with a blank-line delimiter.
504
+ #
505
+ # Any non-2xx response will be treated as a fatal error, and abort the
506
+ # connection. We will set up the status and other response parameters.
507
+ # TODO: we will want to properly support 1xx responses, which some versions
508
+ # of IIS copiously generate.
509
+ # TODO: We need to give the option of not aborting the connection with certain
510
+ # non-200 responses, in order to work with NTLM and other authentication
511
+ # schemes that work at the level of individual connections.
512
+ #
513
+ # Some error responses will get sugarings. For example, we'll return the
514
+ # Location header in the response in case of a 301/302 response.
515
+ #
516
+ # Possible dispositions here:
517
+ # 1) No content to read (either content-length is zero or it's a HEAD request);
518
+ # 2) Switch to text mode to read a specific number of bytes;
519
+ # 3) Read a chunked or multipart response;
520
+ # 4) Read till the server closes the connection.
521
+ #
522
+ # Our reponse to the client can be either to wait till all the content
523
+ # has been read and then to signal caller's deferrable, or else to signal
524
+ # it when we finish the processing the headers and then expect the caller
525
+ # to have given us a block to call as the content comes in. And of course
526
+ # the latter gets stickier with chunks and multiparts.
527
+ #
528
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
529
+ ClenRE = /\AContent-length:\s*(\d+)/i
530
+ def process_received_headers
531
+ abort_connection unless @current_response.headers.first =~ HttpResponseRE
532
+ @current_response.version = $1.dup
533
+ st = $2.dup
534
+ @current_response.status = st.to_i
535
+ abort_connection unless st[0,1] == "2"
536
+
537
+ clen = nil
538
+ @current_response.headers.each do |e|
539
+ if clen == nil and e =~ ClenRE
540
+ clen = $1.dup.to_i
541
+ end
542
+ end
543
+
544
+ if clen
545
+ set_text_mode clen
546
+ end
547
+ end
548
+ private :process_received_headers
549
+
550
+
551
+ def receive_binary_data text
552
+ @current_response.content = text
553
+ @current_response.deferrable.succeed @current_response
554
+ @requests.pop
555
+ @current_response = (@requests.last || []).last
556
+ set_line_mode
557
+ end
558
+
559
+
560
+
561
+ # We've received either a server error or an internal error.
562
+ # Close the connection and abort any pending requests.
563
+ #--
564
+ # When should we call close_connection? It will cause #unbind
565
+ # to be fired. Should the user expect to see #unbind before
566
+ # we call #receive_http_error, or the other way around?
567
+ #
568
+ # Set instance variable @closed. That's used to inhibit further
569
+ # processing of any inbound data after an error has been recognized.
570
+ #
571
+ # We shouldn't have to worry about any leftover outbound data,
572
+ # because we call close_connection (not close_connection_after_writing).
573
+ # That ensures that any pipelined requests received after an error
574
+ # DO NOT get streamed out to the server on this connection.
575
+ # Very important. TODO, write a unit-test to establish that behavior.
576
+ #
577
+ def abort_connection
578
+ close_connection
579
+ @closed = true
580
+ @current_response.deferrable.fail( @current_response )
581
+ end
582
+
583
+
584
+ #------------------------
585
+ # Below here are user-overridable methods.
586
+
587
+ end
588
+ =end
589
+ end
590
+ end