asyncengine 0.0.1.testing1 → 0.0.2.alpha1

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 (251) hide show
  1. data/README.markdown +3 -0
  2. data/Rakefile +38 -0
  3. data/asyncengine.gemspec +8 -4
  4. data/ext/asyncengine/ae_call_from_other_thread.c +106 -0
  5. data/ext/asyncengine/ae_call_from_other_thread.h +12 -0
  6. data/ext/asyncengine/ae_handle_common.c +193 -48
  7. data/ext/asyncengine/ae_handle_common.h +40 -13
  8. data/ext/asyncengine/ae_ip_utils.c +246 -0
  9. data/ext/asyncengine/ae_ip_utils.h +25 -0
  10. data/ext/asyncengine/ae_next_tick.c +81 -21
  11. data/ext/asyncengine/ae_next_tick.h +4 -2
  12. data/ext/asyncengine/ae_resolver.c +156 -0
  13. data/ext/asyncengine/ae_resolver.h +10 -0
  14. data/ext/asyncengine/ae_tcp.c +908 -0
  15. data/ext/asyncengine/ae_tcp.h +20 -0
  16. data/ext/asyncengine/ae_timer.c +355 -81
  17. data/ext/asyncengine/ae_timer.h +11 -4
  18. data/ext/asyncengine/ae_udp.c +579 -13
  19. data/ext/asyncengine/ae_udp.h +15 -2
  20. data/ext/asyncengine/ae_utils.c +192 -0
  21. data/ext/asyncengine/ae_utils.h +16 -0
  22. data/ext/asyncengine/asyncengine_ruby.c +469 -26
  23. data/ext/asyncengine/asyncengine_ruby.h +49 -11
  24. data/ext/asyncengine/debug.h +68 -0
  25. data/ext/asyncengine/extconf.rb +26 -2
  26. data/ext/asyncengine/ip_parser.c +5954 -0
  27. data/ext/asyncengine/ip_parser.h +16 -0
  28. data/ext/asyncengine/libuv/AUTHORS +16 -0
  29. data/ext/asyncengine/libuv/common.gypi +4 -4
  30. data/ext/asyncengine/libuv/config-mingw.mk +6 -6
  31. data/ext/asyncengine/libuv/config-unix.mk +13 -13
  32. data/ext/asyncengine/libuv/gyp_uv +5 -1
  33. data/ext/asyncengine/libuv/ibc_tests/exec_test.sh +8 -0
  34. data/ext/asyncengine/libuv/ibc_tests/uv_shutdown_write_issue.c +171 -0
  35. data/ext/asyncengine/libuv/ibc_tests/uv_tcp_close_while_connecting.c +102 -0
  36. data/ext/asyncengine/libuv/include/uv-private/ngx-queue.h +3 -1
  37. data/ext/asyncengine/libuv/include/uv-private/uv-unix.h +103 -50
  38. data/ext/asyncengine/libuv/include/uv-private/uv-win.h +76 -24
  39. data/ext/asyncengine/libuv/include/uv.h +353 -88
  40. data/ext/asyncengine/libuv/src/ares/ares__close_sockets.o +0 -0
  41. data/ext/asyncengine/libuv/src/ares/ares__get_hostent.o +0 -0
  42. data/ext/asyncengine/libuv/src/ares/ares__read_line.o +0 -0
  43. data/ext/asyncengine/libuv/src/ares/ares__timeval.o +0 -0
  44. data/ext/asyncengine/libuv/src/ares/ares_cancel.o +0 -0
  45. data/ext/asyncengine/libuv/src/ares/ares_data.o +0 -0
  46. data/ext/asyncengine/libuv/src/ares/ares_destroy.o +0 -0
  47. data/ext/asyncengine/libuv/src/ares/ares_expand_name.o +0 -0
  48. data/ext/asyncengine/libuv/src/ares/ares_expand_string.o +0 -0
  49. data/ext/asyncengine/libuv/src/ares/ares_fds.o +0 -0
  50. data/ext/asyncengine/libuv/src/ares/ares_free_hostent.o +0 -0
  51. data/ext/asyncengine/libuv/src/ares/ares_free_string.o +0 -0
  52. data/ext/asyncengine/libuv/src/ares/ares_gethostbyaddr.o +0 -0
  53. data/ext/asyncengine/libuv/src/ares/ares_gethostbyname.o +0 -0
  54. data/ext/asyncengine/libuv/src/ares/ares_getnameinfo.o +0 -0
  55. data/ext/asyncengine/libuv/src/ares/ares_getopt.o +0 -0
  56. data/ext/asyncengine/libuv/src/ares/ares_getsock.o +0 -0
  57. data/ext/asyncengine/libuv/src/ares/ares_init.o +0 -0
  58. data/ext/asyncengine/libuv/src/ares/ares_library_init.o +0 -0
  59. data/ext/asyncengine/libuv/src/ares/ares_llist.o +0 -0
  60. data/ext/asyncengine/libuv/src/ares/ares_mkquery.o +0 -0
  61. data/ext/asyncengine/libuv/src/ares/ares_nowarn.o +0 -0
  62. data/ext/asyncengine/libuv/src/ares/ares_options.o +0 -0
  63. data/ext/asyncengine/libuv/src/ares/ares_parse_a_reply.o +0 -0
  64. data/ext/asyncengine/libuv/src/ares/ares_parse_aaaa_reply.o +0 -0
  65. data/ext/asyncengine/libuv/src/ares/ares_parse_mx_reply.o +0 -0
  66. data/ext/asyncengine/libuv/src/ares/ares_parse_ns_reply.o +0 -0
  67. data/ext/asyncengine/libuv/src/ares/ares_parse_ptr_reply.o +0 -0
  68. data/ext/asyncengine/libuv/src/ares/ares_parse_srv_reply.o +0 -0
  69. data/ext/asyncengine/libuv/src/ares/ares_parse_txt_reply.o +0 -0
  70. data/ext/asyncengine/libuv/src/ares/ares_process.o +0 -0
  71. data/ext/asyncengine/libuv/src/ares/ares_query.o +0 -0
  72. data/ext/asyncengine/libuv/src/ares/ares_search.o +0 -0
  73. data/ext/asyncengine/libuv/src/ares/ares_send.o +0 -0
  74. data/ext/asyncengine/libuv/src/ares/ares_strcasecmp.o +0 -0
  75. data/ext/asyncengine/libuv/src/ares/ares_strdup.o +0 -0
  76. data/ext/asyncengine/libuv/src/ares/ares_strerror.o +0 -0
  77. data/ext/asyncengine/libuv/src/ares/ares_timeout.o +0 -0
  78. data/ext/asyncengine/libuv/src/ares/ares_version.o +0 -0
  79. data/ext/asyncengine/libuv/src/ares/ares_writev.o +0 -0
  80. data/ext/asyncengine/libuv/src/ares/bitncmp.o +0 -0
  81. data/ext/asyncengine/libuv/src/ares/inet_net_pton.o +0 -0
  82. data/ext/asyncengine/libuv/src/ares/inet_ntop.o +0 -0
  83. data/ext/asyncengine/libuv/src/cares.c +225 -0
  84. data/ext/asyncengine/libuv/src/cares.o +0 -0
  85. data/ext/asyncengine/libuv/src/fs-poll.c +237 -0
  86. data/ext/asyncengine/libuv/src/fs-poll.o +0 -0
  87. data/ext/asyncengine/libuv/src/unix/async.c +78 -17
  88. data/ext/asyncengine/libuv/src/unix/async.o +0 -0
  89. data/ext/asyncengine/libuv/src/unix/core.c +305 -213
  90. data/ext/asyncengine/libuv/src/unix/core.o +0 -0
  91. data/ext/asyncengine/libuv/src/unix/cygwin.c +1 -1
  92. data/ext/asyncengine/libuv/src/unix/darwin.c +2 -1
  93. data/ext/asyncengine/libuv/src/unix/dl.c +36 -44
  94. data/ext/asyncengine/libuv/src/unix/dl.o +0 -0
  95. data/ext/asyncengine/libuv/src/unix/eio/eio.o +0 -0
  96. data/ext/asyncengine/libuv/src/unix/error.c +6 -0
  97. data/ext/asyncengine/libuv/src/unix/error.o +0 -0
  98. data/ext/asyncengine/libuv/src/unix/ev/ev.c +8 -4
  99. data/ext/asyncengine/libuv/src/unix/ev/ev.o +0 -0
  100. data/ext/asyncengine/libuv/src/unix/freebsd.c +1 -1
  101. data/ext/asyncengine/libuv/src/unix/fs.c +25 -33
  102. data/ext/asyncengine/libuv/src/unix/fs.o +0 -0
  103. data/ext/asyncengine/libuv/src/unix/internal.h +50 -31
  104. data/ext/asyncengine/libuv/src/unix/kqueue.c +2 -7
  105. data/ext/asyncengine/libuv/src/unix/linux/core.o +0 -0
  106. data/ext/asyncengine/libuv/src/unix/linux/inotify.c +12 -14
  107. data/ext/asyncengine/libuv/src/unix/linux/inotify.o +0 -0
  108. data/ext/asyncengine/libuv/src/unix/linux/{core.c → linux-core.c} +1 -1
  109. data/ext/asyncengine/libuv/src/unix/linux/linux-core.o +0 -0
  110. data/ext/asyncengine/libuv/src/unix/linux/syscalls.c +147 -1
  111. data/ext/asyncengine/libuv/src/unix/linux/syscalls.h +39 -2
  112. data/ext/asyncengine/libuv/src/unix/linux/syscalls.o +0 -0
  113. data/ext/asyncengine/libuv/src/unix/loop-watcher.c +63 -0
  114. data/ext/asyncengine/libuv/src/unix/loop-watcher.o +0 -0
  115. data/ext/asyncengine/libuv/src/unix/loop.c +29 -6
  116. data/ext/asyncengine/libuv/src/unix/loop.o +0 -0
  117. data/ext/asyncengine/libuv/src/unix/netbsd.c +1 -1
  118. data/ext/asyncengine/libuv/src/unix/openbsd.c +1 -1
  119. data/ext/asyncengine/libuv/src/unix/pipe.c +31 -36
  120. data/ext/asyncengine/libuv/src/unix/pipe.o +0 -0
  121. data/ext/asyncengine/libuv/src/unix/poll.c +116 -0
  122. data/ext/asyncengine/libuv/src/unix/poll.o +0 -0
  123. data/ext/asyncengine/libuv/src/unix/process.c +193 -115
  124. data/ext/asyncengine/libuv/src/unix/process.o +0 -0
  125. data/ext/asyncengine/libuv/src/unix/stream.c +146 -153
  126. data/ext/asyncengine/libuv/src/unix/stream.o +0 -0
  127. data/ext/asyncengine/libuv/src/unix/sunos.c +45 -36
  128. data/ext/asyncengine/libuv/src/unix/tcp.c +6 -5
  129. data/ext/asyncengine/libuv/src/unix/tcp.o +0 -0
  130. data/ext/asyncengine/libuv/src/unix/thread.c +82 -25
  131. data/ext/asyncengine/libuv/src/unix/thread.o +0 -0
  132. data/ext/asyncengine/libuv/src/unix/timer.c +69 -58
  133. data/ext/asyncengine/libuv/src/unix/timer.o +0 -0
  134. data/ext/asyncengine/libuv/src/unix/tty.c +3 -3
  135. data/ext/asyncengine/libuv/src/unix/tty.o +0 -0
  136. data/ext/asyncengine/libuv/src/unix/udp.c +57 -66
  137. data/ext/asyncengine/libuv/src/unix/udp.o +0 -0
  138. data/ext/asyncengine/libuv/src/unix/uv-eio.c +33 -50
  139. data/ext/asyncengine/libuv/src/unix/uv-eio.o +0 -0
  140. data/ext/asyncengine/libuv/src/uv-common.c +68 -38
  141. data/ext/asyncengine/libuv/src/uv-common.h +104 -20
  142. data/ext/asyncengine/libuv/src/uv-common.o +0 -0
  143. data/ext/asyncengine/libuv/src/win/async.c +20 -17
  144. data/ext/asyncengine/libuv/src/win/core.c +44 -31
  145. data/ext/asyncengine/libuv/src/win/dl.c +40 -36
  146. data/ext/asyncengine/libuv/src/win/error.c +21 -1
  147. data/ext/asyncengine/libuv/src/win/fs-event.c +19 -21
  148. data/ext/asyncengine/libuv/src/win/fs.c +541 -189
  149. data/ext/asyncengine/libuv/src/win/getaddrinfo.c +56 -63
  150. data/ext/asyncengine/libuv/src/win/handle-inl.h +145 -0
  151. data/ext/asyncengine/libuv/src/win/handle.c +26 -101
  152. data/ext/asyncengine/libuv/src/win/internal.h +92 -107
  153. data/ext/asyncengine/libuv/src/win/loop-watcher.c +6 -14
  154. data/ext/asyncengine/libuv/src/win/pipe.c +78 -64
  155. data/ext/asyncengine/libuv/src/win/poll.c +618 -0
  156. data/ext/asyncengine/libuv/src/win/process-stdio.c +479 -0
  157. data/ext/asyncengine/libuv/src/win/process.c +147 -274
  158. data/ext/asyncengine/libuv/src/win/req-inl.h +225 -0
  159. data/ext/asyncengine/libuv/src/win/req.c +0 -149
  160. data/ext/asyncengine/libuv/src/{unix/check.c → win/stream-inl.h} +31 -42
  161. data/ext/asyncengine/libuv/src/win/stream.c +9 -43
  162. data/ext/asyncengine/libuv/src/win/tcp.c +200 -82
  163. data/ext/asyncengine/libuv/src/win/thread.c +42 -2
  164. data/ext/asyncengine/libuv/src/win/threadpool.c +3 -2
  165. data/ext/asyncengine/libuv/src/win/timer.c +13 -63
  166. data/ext/asyncengine/libuv/src/win/tty.c +26 -20
  167. data/ext/asyncengine/libuv/src/win/udp.c +26 -17
  168. data/ext/asyncengine/libuv/src/win/util.c +312 -167
  169. data/ext/asyncengine/libuv/src/win/winapi.c +16 -1
  170. data/ext/asyncengine/libuv/src/win/winapi.h +33 -9
  171. data/ext/asyncengine/libuv/src/win/winsock.c +88 -1
  172. data/ext/asyncengine/libuv/src/win/winsock.h +36 -3
  173. data/ext/asyncengine/libuv/test/benchmark-ares.c +16 -17
  174. data/ext/asyncengine/libuv/test/benchmark-fs-stat.c +164 -0
  175. data/ext/asyncengine/libuv/test/benchmark-list.h +9 -0
  176. data/ext/asyncengine/libuv/{src/unix/prepare.c → test/benchmark-loop-count.c} +42 -33
  177. data/ext/asyncengine/libuv/test/benchmark-million-timers.c +65 -0
  178. data/ext/asyncengine/libuv/test/benchmark-pound.c +1 -1
  179. data/ext/asyncengine/libuv/test/benchmark-sizes.c +2 -0
  180. data/ext/asyncengine/libuv/test/benchmark-spawn.c +7 -1
  181. data/ext/asyncengine/libuv/test/benchmark-udp-packet-storm.c +1 -1
  182. data/ext/asyncengine/libuv/test/echo-server.c +8 -0
  183. data/ext/asyncengine/libuv/test/run-tests.c +30 -0
  184. data/ext/asyncengine/libuv/test/runner-unix.c +6 -26
  185. data/ext/asyncengine/libuv/test/runner-win.c +5 -63
  186. data/ext/asyncengine/libuv/test/runner.c +10 -1
  187. data/ext/asyncengine/libuv/test/task.h +0 -8
  188. data/ext/asyncengine/libuv/test/test-async.c +43 -141
  189. data/ext/asyncengine/libuv/test/test-callback-order.c +76 -0
  190. data/ext/asyncengine/libuv/test/test-counters-init.c +2 -3
  191. data/ext/asyncengine/libuv/test/test-dlerror.c +17 -8
  192. data/ext/asyncengine/libuv/test/test-fs-event.c +31 -39
  193. data/ext/asyncengine/libuv/test/test-fs-poll.c +146 -0
  194. data/ext/asyncengine/libuv/test/test-fs.c +114 -2
  195. data/ext/asyncengine/libuv/test/test-gethostbyname.c +8 -8
  196. data/ext/asyncengine/libuv/test/test-hrtime.c +18 -15
  197. data/ext/asyncengine/libuv/test/test-ipc.c +8 -2
  198. data/ext/asyncengine/libuv/test/test-list.h +59 -9
  199. data/ext/asyncengine/libuv/test/test-loop-handles.c +2 -25
  200. data/ext/asyncengine/libuv/{src/unix/idle.c → test/test-poll-close.c} +37 -39
  201. data/ext/asyncengine/libuv/test/test-poll.c +573 -0
  202. data/ext/asyncengine/libuv/test/test-ref.c +79 -63
  203. data/ext/asyncengine/libuv/test/test-run-once.c +15 -11
  204. data/ext/asyncengine/libuv/test/test-semaphore.c +111 -0
  205. data/ext/asyncengine/libuv/test/test-spawn.c +368 -20
  206. data/ext/asyncengine/libuv/test/test-stdio-over-pipes.c +25 -35
  207. data/ext/asyncengine/libuv/test/test-tcp-close-while-connecting.c +80 -0
  208. data/ext/asyncengine/libuv/test/test-tcp-close.c +1 -1
  209. data/ext/asyncengine/libuv/test/test-tcp-connect-error-after-write.c +95 -0
  210. data/ext/asyncengine/libuv/test/test-tcp-connect-timeout.c +85 -0
  211. data/ext/asyncengine/libuv/test/test-tcp-shutdown-after-write.c +131 -0
  212. data/ext/asyncengine/libuv/test/test-tcp-write-error.c +2 -2
  213. data/ext/asyncengine/libuv/test/test-tcp-writealot.c +29 -54
  214. data/ext/asyncengine/libuv/test/test-timer-again.c +1 -1
  215. data/ext/asyncengine/libuv/test/test-timer.c +23 -1
  216. data/ext/asyncengine/libuv/test/test-udp-options.c +1 -1
  217. data/ext/asyncengine/libuv/test/{test-eio-overflow.c → test-walk-handles.c} +31 -44
  218. data/ext/asyncengine/libuv/uv.gyp +26 -9
  219. data/ext/asyncengine/rb_utilities.c +54 -0
  220. data/ext/asyncengine/rb_utilities.h +63 -0
  221. data/lib/asyncengine.rb +45 -38
  222. data/lib/asyncengine/asyncengine_ext.so +0 -0
  223. data/lib/asyncengine/debug.rb +37 -0
  224. data/lib/asyncengine/handle.rb +9 -0
  225. data/lib/asyncengine/tcp.rb +28 -0
  226. data/lib/asyncengine/timer.rb +18 -28
  227. data/lib/asyncengine/udp.rb +29 -0
  228. data/lib/asyncengine/utils.rb +32 -0
  229. data/lib/asyncengine/uv_error.rb +17 -0
  230. data/lib/asyncengine/version.rb +9 -1
  231. data/test/ae_test_helper.rb +62 -0
  232. data/test/test_basic.rb +169 -0
  233. data/test/test_call_from_other_thread.rb +55 -0
  234. data/test/test_error.rb +92 -0
  235. data/test/test_ip_utils.rb +44 -0
  236. data/test/test_next_tick.rb +37 -0
  237. data/test/test_resolver.rb +51 -0
  238. data/test/test_threads.rb +69 -0
  239. data/test/test_timer.rb +95 -0
  240. data/test/test_udp.rb +216 -0
  241. data/test/test_utils.rb +49 -0
  242. metadata +84 -57
  243. data/ext/asyncengine/libuv/mkmf.log +0 -24
  244. data/ext/asyncengine/libuv/src/unix/cares.c +0 -194
  245. data/ext/asyncengine/libuv/src/unix/cares.o +0 -0
  246. data/ext/asyncengine/libuv/src/unix/check.o +0 -0
  247. data/ext/asyncengine/libuv/src/unix/idle.o +0 -0
  248. data/ext/asyncengine/libuv/src/unix/prepare.o +0 -0
  249. data/ext/asyncengine/libuv/src/win/cares.c +0 -290
  250. data/lib/asyncengine/errors.rb +0 -5
  251. data/lib/asyncengine/next_tick.rb +0 -24
@@ -2,9 +2,11 @@
2
2
  #define AE_NEXT_TICK_H
3
3
 
4
4
 
5
- void init_ae_next_tick();
5
+ void init_ae_next_tick(void);
6
+ void load_ae_next_tick_uv_idle(void);
7
+ void unload_ae_next_tick_uv_idle(void);
6
8
 
7
- VALUE AsyncEngine_c_next_tick(VALUE self);
9
+ static VALUE AsyncEngine_next_tick(int argc, VALUE *argv, VALUE self);
8
10
 
9
11
 
10
12
  #endif /* AE_NEXT_TICK_H */
@@ -0,0 +1,156 @@
1
+ #include "asyncengine_ruby.h"
2
+ #include "ae_handle_common.h"
3
+ #include "ae_resolver.h"
4
+
5
+
6
+ static VALUE cAsyncEngineResolver;
7
+
8
+
9
+ typedef struct {
10
+ VALUE on_result_proc_id;
11
+ } struct_getaddrinfo_data;
12
+
13
+ struct _uv_getaddrinfo_callback_data {
14
+ int status;
15
+ struct addrinfo* res;
16
+ VALUE on_result_proc_id;
17
+ };
18
+
19
+
20
+ // Used for storing information about the last getaddrinfo() callback.
21
+ static struct _uv_getaddrinfo_callback_data last_uv_getaddrinfo_callback_data;
22
+
23
+
24
+ /** Predeclaration of static functions. */
25
+
26
+ static void _uv_getaddrinfo_callback(uv_getaddrinfo_t* handle, int status, struct addrinfo* res);
27
+ static VALUE _ae_getaddrinfo_callback(void);
28
+
29
+
30
+ void init_ae_resolver()
31
+ {
32
+ AE_TRACE();
33
+
34
+ cAsyncEngineResolver = rb_define_class_under(mAsyncEngine, "Resolver", cAsyncEngineHandle);
35
+
36
+ rb_define_singleton_method(cAsyncEngineResolver, "resolve", AsyncEngineResolver_resolve, -1);
37
+ }
38
+
39
+
40
+ /** Resolver.resolve() class method.
41
+ *
42
+ * Arguments:
43
+ * - hostname (String).
44
+ * - IP family (Fixnum), valid values: Socket::AF_INET, Socket::AF_INET6, Socket::AF_UNSPEC (optional).
45
+ * - Callback (Proc) (optional).
46
+ *
47
+ * Block optional.
48
+ */
49
+
50
+ static
51
+ VALUE AsyncEngineResolver_resolve(int argc, VALUE *argv, VALUE self)
52
+ {
53
+ AE_TRACE();
54
+ uv_getaddrinfo_t* _uv_handle;
55
+ struct_getaddrinfo_data* data;
56
+ char *hostname;
57
+ struct addrinfo addrinfo_hints;
58
+ VALUE proc;
59
+ int r;
60
+
61
+ AE_CHECK_STATUS();
62
+ AE_RB_CHECK_NUM_ARGS(1,3);
63
+ AE_RB_ENSURE_BLOCK_OR_PROC(3, proc);
64
+
65
+ // Parameter 1: hostname.
66
+ if (! RB_TYPE_P(argv[0], T_STRING))
67
+ rb_raise(rb_eTypeError, "hostname must be a String");
68
+ hostname = StringValueCStr(argv[0]);
69
+
70
+ memset(&addrinfo_hints, 0, sizeof(addrinfo_hints));
71
+ addrinfo_hints.ai_socktype = SOCK_STREAM; // Avoid duplicated results (with some IP but different socktype).
72
+
73
+ // Parameter 2: IP family (optional).
74
+ if (argc > 1 && ! NIL_P(argv[1])) {
75
+ if (! FIXNUM_P(argv[1]))
76
+ rb_raise(rb_eTypeError, "IP family must be a Fixnum");
77
+ addrinfo_hints.ai_family = FIX2INT(argv[1]);
78
+ }
79
+
80
+ _uv_handle = ALLOC(uv_getaddrinfo_t);
81
+ data = ALLOC(struct_getaddrinfo_data);
82
+ _uv_handle->data = (void*)data;
83
+
84
+ r = uv_getaddrinfo(AE_uv_loop, _uv_handle, &_uv_getaddrinfo_callback, hostname, NULL, &addrinfo_hints);
85
+ if (r != 0) {
86
+ xfree(data);
87
+ xfree(_uv_handle);
88
+ ae_raise_last_uv_error();
89
+ }
90
+
91
+ data->on_result_proc_id = ae_store_proc(proc);
92
+
93
+ return Qtrue;
94
+ }
95
+
96
+
97
+ static
98
+ void _uv_getaddrinfo_callback(uv_getaddrinfo_t* handle, int status, struct addrinfo* res)
99
+ {
100
+ AE_TRACE();
101
+ struct_getaddrinfo_data* data = (struct_getaddrinfo_data*)handle->data;
102
+
103
+ last_uv_getaddrinfo_callback_data.status = status;
104
+ last_uv_getaddrinfo_callback_data.res = res;
105
+ last_uv_getaddrinfo_callback_data.on_result_proc_id = data->on_result_proc_id;
106
+
107
+ xfree(handle->data);
108
+ xfree(handle);
109
+
110
+ ae_take_gvl_and_run_with_error_handler(_ae_getaddrinfo_callback);
111
+ }
112
+
113
+
114
+ static
115
+ VALUE _ae_getaddrinfo_callback(void)
116
+ {
117
+ AE_TRACE();
118
+ struct addrinfo *ptr;
119
+ struct sockaddr_in *addr4;
120
+ struct sockaddr_in6 *addr6;
121
+ char ip[INET6_ADDRSTRLEN];
122
+ VALUE ips;
123
+ VALUE proc;
124
+
125
+ if (last_uv_getaddrinfo_callback_data.status == 0) {
126
+ ips = rb_ary_new();
127
+ for (ptr = last_uv_getaddrinfo_callback_data.res; ptr; ptr = ptr->ai_next) {
128
+ if (ptr->ai_addrlen == 0)
129
+ continue;
130
+ switch (ptr->ai_addr->sa_family) {
131
+ case AF_INET:
132
+ addr4 = (struct sockaddr_in*)ptr->ai_addr;
133
+ uv_ip4_name(addr4, ip, INET_ADDRSTRLEN);
134
+ rb_ary_push(ips, rb_str_new2(ip));
135
+ break;
136
+ case AF_INET6:
137
+ addr6 = (struct sockaddr_in6*)ptr->ai_addr;
138
+ uv_ip6_name(addr6, ip, INET6_ADDRSTRLEN);
139
+ rb_ary_push(ips, rb_str_new2(ip));
140
+ break;
141
+ }
142
+ }
143
+ }
144
+
145
+ uv_freeaddrinfo(last_uv_getaddrinfo_callback_data.res);
146
+
147
+ // Don't execute the callback when AsyncEngine is releasing.
148
+ if (AE_status == AE_RELEASING)
149
+ return Qnil;
150
+
151
+ proc = ae_remove_proc(last_uv_getaddrinfo_callback_data.on_result_proc_id);
152
+ if (last_uv_getaddrinfo_callback_data.status == 0)
153
+ return ae_proc_call_2(proc, Qnil, ips);
154
+ else
155
+ return ae_proc_call_2(proc, ae_get_last_uv_error(), Qnil);
156
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef AE_RESOLVER_H
2
+ #define AE_RESOLVER_H
3
+
4
+
5
+ void init_ae_resolver(void);
6
+
7
+ static VALUE AsyncEngineResolver_resolve(int argc, VALUE *argv, VALUE self);
8
+
9
+
10
+ #endif /* AE_RESOLVER_H */
@@ -0,0 +1,908 @@
1
+ #include "asyncengine_ruby.h"
2
+ #include "ae_handle_common.h"
3
+ #include "ae_ip_utils.h"
4
+ #include "ae_tcp.h"
5
+
6
+
7
+ static VALUE cAsyncEngineTcpSocket;
8
+
9
+ static ID method_on_connected;
10
+ static ID method_on_connection_error;
11
+ static ID method_on_data_received;
12
+ static ID method_on_disconnected;
13
+
14
+ static VALUE symbol_closed;
15
+ static VALUE symbol_connecting;
16
+ static VALUE symbol_connected;
17
+
18
+
19
+ enum status {
20
+ CONNECTING = 0,
21
+ CONNECTED
22
+ };
23
+
24
+ // TODO: methods pause(), resume() and paused?().
25
+ enum flags {
26
+ RELEASING = 1 << 1,
27
+ PAUSED = 1 << 2,
28
+ SHUTDOWN_REQUESTED = 1 << 3,
29
+ CONNECT_TIMEOUT = 1 << 4,
30
+ EOF_RECEIVED = 1 << 5
31
+ };
32
+
33
+ typedef struct {
34
+ uv_tcp_t *_uv_handle;
35
+ enum_ip_type ip_type;
36
+ unsigned int status;
37
+ unsigned int flags;
38
+ enum_string_encoding encoding;
39
+ uv_timer_t *_uv_timer_connect_timeout;
40
+ VALUE ae_handle;
41
+ VALUE ae_handle_id;
42
+ } struct_cdata;
43
+
44
+ struct _uv_connect_callback_data {
45
+ struct_cdata* cdata;
46
+ int status;
47
+ };
48
+
49
+ struct _uv_read_callback_data {
50
+ struct_cdata* cdata;
51
+ ssize_t nread;
52
+ uv_buf_t buf;
53
+ };
54
+
55
+ typedef struct {
56
+ uv_buf_t buf;
57
+ VALUE *on_write_block;
58
+ } struct_uv_write_req_data;
59
+
60
+ struct _uv_write_callback_data {
61
+ struct_cdata* cdata;
62
+ int status;
63
+ VALUE *on_write_block;
64
+ };
65
+
66
+ struct _uv_shutdown_callback_data {
67
+ struct_cdata* cdata;
68
+ };
69
+
70
+ struct _uv_timer_connect_timeout_callback_data {
71
+ struct_cdata* cdata;
72
+ };
73
+
74
+ // Used for storing information about the last TCP connect callback.
75
+ static struct _uv_connect_callback_data last_uv_connect_callback_data;
76
+
77
+ // Used for storing information about the last TCP read callback.
78
+ static struct _uv_read_callback_data last_uv_read_callback_data;
79
+
80
+ // Used for storing information about the last TCP write callback.
81
+ static struct _uv_write_callback_data last_uv_write_callback_data;
82
+
83
+ // Used for storing information about the last TCP shutdown callback.
84
+ static struct _uv_shutdown_callback_data last_uv_shutdown_callback_data;
85
+
86
+ // Used for storing information about the last TCP connection timeout callback.
87
+ static struct _uv_timer_connect_timeout_callback_data last_uv_timer_connect_timeout_callback_data;
88
+
89
+
90
+ /** Predeclaration of static functions. */
91
+
92
+ static VALUE AsyncEngineTcpSocket_alloc(VALUE klass);
93
+ static void AsyncEngineTcpSocket_free(struct_cdata* cdata);
94
+ static void init_instance(VALUE self, enum_ip_type ip_type, char* dest_ip, int dest_port, char* bind_ip, int bind_port);
95
+ static void _uv_connect_callback(uv_connect_t* req, int status);
96
+ static VALUE _ae_connect_callback(void);
97
+ static uv_buf_t _uv_alloc_callback(uv_handle_t* handle, size_t suggested_size);
98
+ static void _uv_read_callback(uv_stream_t* stream, ssize_t nread, uv_buf_t buf);
99
+ static VALUE _ae_read_callback(void);
100
+ static void _uv_read_callback(uv_stream_t* stream, ssize_t nread, uv_buf_t buf);
101
+ static void _uv_write_callback(uv_write_t* req, int status);
102
+ static VALUE _ae_write_callback(void);
103
+ static void _uv_timer_connect_timeout_callback(uv_timer_t* handle, int status);
104
+ static void _ae_cancel_timer_connect_timeout(struct_cdata* cdata);
105
+ static void _uv_shutdown_callback(uv_shutdown_t* req, int status);
106
+ static VALUE _ae_shutdown_callback(void);
107
+
108
+
109
+ void init_ae_tcp()
110
+ {
111
+ AE_TRACE();
112
+
113
+ cAsyncEngineTcpSocket = rb_define_class_under(mAsyncEngine, "TCPSocket", cAsyncEngineHandle);
114
+
115
+ rb_define_alloc_func(cAsyncEngineTcpSocket, AsyncEngineTcpSocket_alloc);
116
+ rb_define_singleton_method(cAsyncEngineTcpSocket, "new", AsyncEngineTcpSocket_new, -1);
117
+
118
+ rb_define_method(cAsyncEngineTcpSocket, "send_data", AsyncEngineTcpSocket_send_data, -1);
119
+ rb_define_method(cAsyncEngineTcpSocket, "local_address", AsyncEngineTcpSocket_local_address, 0);
120
+ rb_define_method(cAsyncEngineTcpSocket, "peer_address", AsyncEngineTcpSocket_peer_address, 0);
121
+ rb_define_method(cAsyncEngineTcpSocket, "set_connect_timeout", AsyncEngineTcpSocket_set_connect_timeout, 1);
122
+ rb_define_alias(cAsyncEngineTcpSocket, "connect_timeout=", "set_connect_timeout");
123
+ rb_define_method(cAsyncEngineTcpSocket, "status", AsyncEngineTcpSocket_status, 0);
124
+ rb_define_method(cAsyncEngineTcpSocket, "connected?", AsyncEngineTcpSocket_is_connected, 0);
125
+ rb_define_method(cAsyncEngineTcpSocket, "alive?", AsyncEngineTcpSocket_is_alive, 0);
126
+ rb_define_method(cAsyncEngineTcpSocket, "close", AsyncEngineTcpSocket_close, 0);
127
+ rb_define_method(cAsyncEngineTcpSocket, "close_gracefully", AsyncEngineTcpSocket_close_gracefully, -1);
128
+ rb_define_private_method(cAsyncEngineTcpSocket, "destroy", AsyncEngineTcpSocket_destroy, 0);
129
+
130
+ method_on_connected = rb_intern("on_connected");
131
+ method_on_connection_error = rb_intern("on_connection_error");
132
+ method_on_data_received = rb_intern("on_data_received");
133
+ method_on_disconnected = rb_intern("on_disconnected");
134
+
135
+ symbol_closed = ID2SYM(rb_intern("closed"));
136
+ symbol_connecting = ID2SYM(rb_intern("connecting"));
137
+ symbol_connected = ID2SYM(rb_intern("connected"));
138
+ }
139
+
140
+
141
+ /** Class alloc() and free() functions. */
142
+
143
+ static
144
+ VALUE AsyncEngineTcpSocket_alloc(VALUE klass)
145
+ {
146
+ AE_TRACE();
147
+
148
+ struct_cdata* cdata = ALLOC(struct_cdata);
149
+
150
+ return Data_Wrap_Struct(klass, NULL, AsyncEngineTcpSocket_free, cdata);
151
+ }
152
+
153
+
154
+ static
155
+ void AsyncEngineTcpSocket_free(struct_cdata* cdata)
156
+ {
157
+ AE_TRACE2();
158
+
159
+ xfree(cdata);
160
+ }
161
+
162
+
163
+ /** TCPSocket.new() method.
164
+ *
165
+ * Arguments:
166
+ * - destination IP (String).
167
+ * - destination port (Fixnum).
168
+ * - bind IP (String or Nil) (optional).
169
+ * - bind port (Fixnum or Nil) (optional).
170
+ */
171
+
172
+ static
173
+ VALUE AsyncEngineTcpSocket_new(int argc, VALUE *argv, VALUE self)
174
+ {
175
+ AE_TRACE();
176
+ char *dest_ip, *bind_ip;
177
+ int dest_ip_len, bind_ip_len;
178
+ int dest_port, bind_port;
179
+ enum_ip_type ip_type, bind_ip_type;
180
+ VALUE instance;
181
+
182
+ AE_CHECK_STATUS();
183
+ AE_RB_CHECK_NUM_ARGS(2,4);
184
+
185
+ // Parameter 1: destination IP.
186
+ if (! RB_TYPE_P(argv[0], T_STRING))
187
+ rb_raise(rb_eTypeError, "destination IP must be a String");
188
+ dest_ip = StringValueCStr(argv[0]);
189
+ dest_ip_len = RSTRING_LEN(argv[0]);
190
+
191
+ // Parameter 2: destination port.
192
+ if (! FIXNUM_P(argv[1]))
193
+ rb_raise(rb_eTypeError, "destination port must be a Fixnum");
194
+ dest_port = FIX2INT(argv[1]);
195
+ if (! ae_ip_utils_is_valid_port(dest_port))
196
+ rb_raise(rb_eArgError, "invalid destination port value");
197
+
198
+ bind_ip = NULL;
199
+ bind_port = 0;
200
+
201
+ // Parameter 3: bind IP.
202
+ if (argc >= 3) {
203
+ switch(TYPE(argv[2])) {
204
+ case T_STRING:
205
+ bind_ip = StringValueCStr(argv[2]);
206
+ bind_ip_len = RSTRING_LEN(argv[2]);
207
+ break;
208
+ case T_NIL:
209
+ break;
210
+ default:
211
+ rb_raise(rb_eTypeError, "bind IP must be a String");
212
+ }
213
+
214
+ // Parameter 4: bind port.
215
+ if (bind_ip && argc == 4) {
216
+ switch(TYPE(argv[3])) {
217
+ case T_FIXNUM:
218
+ bind_port = FIX2INT(argv[3]);
219
+ if (! ae_ip_utils_is_valid_port(bind_port))
220
+ rb_raise(rb_eArgError, "invalid bind port value");
221
+ break;
222
+ case T_NIL:
223
+ break;
224
+ default:
225
+ rb_raise(rb_eTypeError, "bind port must be a Fixnum");
226
+ break;
227
+ }
228
+ }
229
+ }
230
+
231
+ // TODO: Allow domains y blablablablablablabla puto sag.
232
+ // Parse destination IP.
233
+ if ((ip_type = ae_ip_parser_execute(dest_ip, dest_ip_len)) == ip_type_no_ip)
234
+ rb_raise(rb_eTypeError, "destination IP is not valid IPv4 or IPv6");
235
+
236
+ // Parse bind IP.
237
+ if (bind_ip) {
238
+ if ((bind_ip_type = ae_ip_parser_execute(bind_ip, bind_ip_len)) == ip_type_no_ip)
239
+ rb_raise(rb_eTypeError, "bind IP is not valid IPv4 or IPv6");
240
+ else if (bind_ip_type != ip_type)
241
+ rb_raise(rb_eTypeError, "bind IP does not belong to the same IP family of the destination IP");
242
+ }
243
+
244
+ // Allocate the Ruby instance.
245
+ instance = rb_obj_alloc(self);
246
+
247
+ // Init the UV stuff within the instance.
248
+ init_instance(instance, ip_type, dest_ip, dest_port, bind_ip, bind_port);
249
+
250
+ return instance;
251
+ }
252
+
253
+
254
+ static
255
+ void init_instance(VALUE self, enum_ip_type ip_type, char *dest_ip, int dest_port, char *bind_ip, int bind_port)
256
+ {
257
+ AE_TRACE();
258
+ uv_tcp_t *_uv_handle;
259
+ uv_connect_t *_uv_tcp_connect_req = NULL;
260
+ int ret;
261
+
262
+ // Create and init the UV handle.
263
+ _uv_handle = ALLOC(uv_tcp_t);
264
+ if (uv_tcp_init(AE_uv_loop, _uv_handle)) {
265
+ xfree(_uv_handle);
266
+ ae_raise_last_uv_error();
267
+ }
268
+
269
+ // Bind to the given IP (and optional port) if given.
270
+ if (bind_ip) {
271
+ switch(ip_type) {
272
+ case ip_type_ipv4:
273
+ ret = uv_tcp_bind(_uv_handle, uv_ip4_addr(bind_ip, bind_port));
274
+ break;
275
+ case ip_type_ipv6:
276
+ ret = uv_tcp_bind6(_uv_handle, uv_ip6_addr(bind_ip, bind_port));
277
+ break;
278
+ }
279
+ if (ret) {
280
+ AE_CLOSE_UV_HANDLE(_uv_handle);
281
+ ae_raise_last_uv_error();
282
+ }
283
+ }
284
+
285
+ // Connect.
286
+ _uv_tcp_connect_req = ALLOC(uv_connect_t);
287
+
288
+ switch(ip_type) {
289
+ case ip_type_ipv4:
290
+ ret = uv_tcp_connect(_uv_tcp_connect_req, _uv_handle, uv_ip4_addr(dest_ip, dest_port), _uv_connect_callback);
291
+ break;
292
+ case ip_type_ipv6:
293
+ ret = uv_tcp_connect6(_uv_tcp_connect_req, _uv_handle, uv_ip6_addr(dest_ip, dest_port), _uv_connect_callback);
294
+ break;
295
+ }
296
+ if (ret) {
297
+ xfree(_uv_tcp_connect_req);
298
+ AE_CLOSE_UV_HANDLE(_uv_handle);
299
+ ae_raise_last_uv_error();
300
+ }
301
+
302
+ // Fill cdata struct.
303
+ GET_CDATA_FROM_SELF;
304
+ cdata->_uv_handle = _uv_handle;
305
+ cdata->ae_handle = self;
306
+ cdata->ae_handle_id = ae_store_handle(self); // Avoid GC.
307
+ cdata->ip_type = ip_type;
308
+ cdata->status = CONNECTING;
309
+ cdata->flags = 0;
310
+ cdata->encoding = string_encoding_ascii;
311
+ cdata->_uv_timer_connect_timeout = NULL;
312
+
313
+ // Fill data field of the UV handle.
314
+ cdata->_uv_handle->data = (void *)cdata;
315
+ }
316
+
317
+
318
+ /** TCP connect callback. */
319
+
320
+ static
321
+ void _uv_connect_callback(uv_connect_t* req, int status)
322
+ {
323
+ AE_TRACE2();
324
+
325
+ uv_tcp_t* _uv_handle = (uv_tcp_t*)req->handle;
326
+ struct_cdata* cdata = (struct_cdata*)_uv_handle->data;
327
+ uv_shutdown_t *shutdown_req;
328
+
329
+ xfree(req);
330
+
331
+ last_uv_connect_callback_data.cdata = cdata;
332
+ last_uv_connect_callback_data.status = status;
333
+
334
+ if (cdata->_uv_timer_connect_timeout)
335
+ _ae_cancel_timer_connect_timeout(cdata);
336
+
337
+ // Connection established.
338
+ if (! status) {
339
+ // If gracefull close was requested while connecting, do it now.
340
+ if (cdata->flags & SHUTDOWN_REQUESTED) {
341
+ AE_ASSERT(! uv_read_stop((uv_stream_t*)cdata->_uv_handle));
342
+ shutdown_req = ALLOC(uv_shutdown_t);
343
+ // TODO: Muy bestia?
344
+ AE_ASSERT(! uv_shutdown(shutdown_req, (uv_stream_t*)cdata->_uv_handle, _uv_shutdown_callback));
345
+ }
346
+ // Otherwise start reading.
347
+ else
348
+ AE_ASSERT(! uv_read_start((uv_stream_t*)_uv_handle, _uv_alloc_callback, _uv_read_callback));
349
+
350
+ cdata->status = CONNECTED;
351
+ }
352
+ // If it's a connection error and uv_is_closing() == 1 it means that the AE handle
353
+ // was closed by the application, so don't close the UV handle again. Otherwise
354
+ // (uv_is_closing() == 0) close the UV handle.
355
+ else if (! uv_is_closing((const uv_handle_t*)_uv_handle)) {
356
+ AE_CLOSE_UV_HANDLE(_uv_handle);
357
+ cdata->_uv_handle = NULL;
358
+ }
359
+
360
+ ae_take_gvl_and_run_with_error_handler(_ae_connect_callback);
361
+ }
362
+
363
+
364
+ static
365
+ VALUE _ae_connect_callback(void)
366
+ {
367
+ AE_TRACE2();
368
+
369
+ struct_cdata* cdata = last_uv_connect_callback_data.cdata;
370
+ VALUE error;
371
+
372
+ // Connection succedded, so call on_connected() callback in the instance.
373
+ if (! last_uv_connect_callback_data.status) {
374
+ rb_funcall2(cdata->ae_handle, method_on_connected, 0, NULL);
375
+ }
376
+ // Connection failed.
377
+ else {
378
+ ae_remove_handle(cdata->ae_handle_id);
379
+
380
+ // Network error, remote rejection, client closed before connecting or AE releasing.
381
+ if (! (cdata->flags & CONNECT_TIMEOUT))
382
+ error = ae_get_last_uv_error();
383
+ // Connection timeout set by the user, so raise UV error 40: ETIMEDOUT, "connection timed out".
384
+ else
385
+ error = ae_get_uv_error(UV_ETIMEDOUT);
386
+
387
+ rb_funcall2(cdata->ae_handle, method_on_connection_error, 1, &error);
388
+ }
389
+
390
+ return Qnil;
391
+ }
392
+
393
+
394
+ /** TCP read callback. */
395
+
396
+ static
397
+ uv_buf_t _uv_alloc_callback(uv_handle_t* handle, size_t suggested_size)
398
+ {
399
+ AE_TRACE();
400
+
401
+ return uv_buf_init(ALLOC_N(char, suggested_size), suggested_size);
402
+ }
403
+
404
+
405
+ static
406
+ void _uv_read_callback(uv_stream_t* stream, ssize_t nread, uv_buf_t buf)
407
+ {
408
+ AE_TRACE2();
409
+
410
+ uv_tcp_t *_uv_handle = (uv_tcp_t *)stream;
411
+ struct_cdata* cdata = (struct_cdata*)_uv_handle->data;
412
+ uv_shutdown_t *shutdown_req;
413
+
414
+ last_uv_read_callback_data.cdata = cdata;
415
+ last_uv_read_callback_data.nread = nread;
416
+ last_uv_read_callback_data.buf = buf;
417
+
418
+ /*
419
+ * nread 0: libuv requested a buffer through the alloc callback but then decided
420
+ * that it didn't need that buffer.
421
+ * nread -1: disconnecton, so close the UV handle.
422
+ *
423
+ * In any case, read alloc cb *may* be previously called so check the buffer pointer
424
+ * and in case it's not NULL, free it.
425
+ */
426
+ if (nread == 0) {
427
+ if (buf.base)
428
+ xfree(buf.base);
429
+ }
430
+ else if (nread == -1) {
431
+ if (buf.base)
432
+ xfree(buf.base);
433
+
434
+ /*
435
+ * Once the connection is established, if the app calls to uv_close() this tcp_read_callback
436
+ * could be called with nread=0, but never with nread=-1, so -1 means "network/remote disconnection".
437
+ *
438
+ * If the error is EOF it means that the peer has close its reading but we could still write to it
439
+ * so instead of closing the handle, call to uv_shutdown() so pending write reqs can terminate. Then
440
+ * wait to shutdown_cb() for closing the handle.
441
+ */
442
+ if (ae_get_last_uv_error_int() == UV_EOF) {
443
+ // First stop reading (not sure if it's needed, but I don't want to receive a ECONNRESET later).
444
+ AE_ASSERT(! uv_read_stop((uv_stream_t*)_uv_handle));
445
+ shutdown_req = ALLOC(uv_shutdown_t);
446
+ AE_ASSERT(! uv_shutdown(shutdown_req, (uv_stream_t*)cdata->_uv_handle, _uv_shutdown_callback));
447
+ cdata->flags |= EOF_RECEIVED;
448
+ }
449
+ else {
450
+ AE_CLOSE_UV_HANDLE(_uv_handle);
451
+ cdata->_uv_handle = NULL;
452
+ ae_take_gvl_and_run_with_error_handler(_ae_read_callback);
453
+ }
454
+ }
455
+ else {
456
+ if (! (cdata->flags & PAUSED))
457
+ ae_take_gvl_and_run_with_error_handler(_ae_read_callback);
458
+ else
459
+ xfree(buf.base);
460
+ }
461
+ }
462
+
463
+
464
+ static
465
+ VALUE _ae_read_callback(void)
466
+ {
467
+ AE_TRACE2();
468
+
469
+ struct_cdata* cdata = last_uv_read_callback_data.cdata;
470
+ VALUE _rb_data;
471
+ VALUE error;
472
+
473
+ // Data received.
474
+ if (last_uv_read_callback_data.nread > 0) {
475
+ _rb_data = ae_rb_str_new(last_uv_read_callback_data.buf.base, last_uv_read_callback_data.nread, cdata->encoding, 1);
476
+ xfree(last_uv_read_callback_data.buf.base);
477
+ rb_funcall2(cdata->ae_handle, method_on_data_received, 1, &_rb_data);
478
+ }
479
+ // Otherwise it's -1 (EOF, disconnection).
480
+ else {
481
+ error = ae_get_last_uv_error();
482
+ ae_remove_handle(cdata->ae_handle_id);
483
+ rb_funcall2(cdata->ae_handle, method_on_disconnected, 1, &error);
484
+ }
485
+
486
+ return Qnil;
487
+ }
488
+
489
+
490
+ /** TCPSocket#send_data() method. */
491
+
492
+ static
493
+ VALUE AsyncEngineTcpSocket_send_data(int argc, VALUE *argv, VALUE self)
494
+ {
495
+ AE_TRACE2();
496
+
497
+ char *data = NULL;
498
+ int data_len;
499
+ VALUE _rb_block;
500
+ uv_write_t* _uv_write_req = NULL;
501
+ uv_buf_t buf;
502
+ struct_uv_write_req_data* _uv_write_req_data = NULL;
503
+
504
+ AE_RB_CHECK_NUM_ARGS(1,2);
505
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
506
+ AE_RB_GET_BLOCK_OR_PROC(2, _rb_block);
507
+
508
+ // Parameter 1: data.
509
+ if (! RB_TYPE_P(argv[0], T_STRING))
510
+ rb_raise(rb_eTypeError, "data must be a String");
511
+
512
+ data_len = RSTRING_LEN(argv[0]);
513
+ data = ALLOC_N(char, data_len);
514
+ memcpy(data, RSTRING_PTR(argv[0]), data_len);
515
+
516
+ buf = uv_buf_init(data, data_len);
517
+
518
+ _uv_write_req_data = ALLOC(struct_uv_write_req_data);
519
+ _uv_write_req_data->buf = buf;
520
+
521
+ if (! NIL_P(_rb_block)) {
522
+ _uv_write_req_data->on_write_block = ALLOC(VALUE);
523
+ *(_uv_write_req_data->on_write_block) = _rb_block;
524
+ rb_gc_register_address(_uv_write_req_data->on_write_block);
525
+ }
526
+ else
527
+ _uv_write_req_data->on_write_block = NULL;
528
+
529
+ _uv_write_req = ALLOC(uv_write_t);
530
+ _uv_write_req->data = _uv_write_req_data;
531
+
532
+ if (uv_write(_uv_write_req, (uv_stream_t *)cdata->_uv_handle, &buf, 1, _uv_write_callback)) {
533
+ xfree(data);
534
+ if (_uv_write_req_data->on_write_block) {
535
+ rb_gc_unregister_address(_uv_write_req_data->on_write_block);
536
+ xfree(_uv_write_req_data->on_write_block);
537
+ }
538
+ xfree(_uv_write_req_data);
539
+ xfree(_uv_write_req);
540
+ ae_raise_last_uv_error();
541
+ }
542
+
543
+ return Qtrue;
544
+ }
545
+
546
+
547
+ static
548
+ void _uv_write_callback(uv_write_t* req, int status)
549
+ {
550
+ AE_TRACE2();
551
+
552
+ uv_tcp_t *_uv_handle = (uv_tcp_t*)req->handle;
553
+ struct_uv_write_req_data* _uv_write_req_data = (struct_uv_write_req_data*)req->data;
554
+ struct_cdata* cdata = (struct_cdata*)_uv_handle->data;
555
+ int has_block = 0;
556
+
557
+ // Block was provided so must go to Ruby land.
558
+ if (_uv_write_req_data->on_write_block) {
559
+ last_uv_write_callback_data.cdata = cdata;
560
+ last_uv_write_callback_data.status = status;
561
+ last_uv_write_callback_data.on_write_block = _uv_write_req_data->on_write_block;
562
+ has_block = 1;
563
+ }
564
+
565
+ xfree(_uv_write_req_data->buf.base);
566
+ xfree(_uv_write_req_data);
567
+ xfree(req);
568
+
569
+ // NOTE: Even if the handle has been destroyed, we need to go to Ruby land to
570
+ // unregister and free the write block.
571
+ if (has_block)
572
+ ae_take_gvl_and_run_with_error_handler(_ae_write_callback);
573
+ }
574
+
575
+
576
+ static
577
+ VALUE _ae_write_callback(void)
578
+ {
579
+ AE_TRACE2();
580
+
581
+ VALUE rb_on_write_block = *(last_uv_write_callback_data.on_write_block);
582
+
583
+ rb_gc_unregister_address(last_uv_write_callback_data.on_write_block);
584
+ xfree(last_uv_write_callback_data.on_write_block);
585
+
586
+ // Don't run the write block when releasing.
587
+ if (! (last_uv_write_callback_data.cdata->flags & RELEASING)) {
588
+ if (! last_uv_write_callback_data.status)
589
+ ae_proc_call_1(rb_on_write_block, Qnil);
590
+ else
591
+ ae_proc_call_1(rb_on_write_block, ae_get_last_uv_error());
592
+ }
593
+
594
+ return Qnil;
595
+ }
596
+
597
+
598
+ /** TCPSocket#local_address() and TCPSocket#peer_address() methods. */
599
+
600
+ static
601
+ VALUE AsyncEngineTcpSocket_local_address(VALUE self)
602
+ {
603
+ AE_TRACE();
604
+
605
+ struct sockaddr_storage local_addr;
606
+ int len = sizeof(local_addr);
607
+
608
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
609
+
610
+ // NOTE: If disconnection occurs just before the tcp read callback (with nread = -1)
611
+ // this would fail with ENOTCONN, "socket is not connected". In this case
612
+ // return nil.
613
+ if (uv_tcp_getsockname(cdata->_uv_handle, (struct sockaddr*)&local_addr, &len))
614
+ return Qnil;
615
+
616
+ return ae_ip_utils_get_ip_port(&local_addr, cdata->ip_type);
617
+ }
618
+
619
+
620
+ static
621
+ VALUE AsyncEngineTcpSocket_peer_address(VALUE self)
622
+ {
623
+ AE_TRACE();
624
+
625
+ struct sockaddr_storage peer_addr;
626
+ int len = sizeof(peer_addr);
627
+ VALUE _rb_array_ip_port;
628
+
629
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
630
+
631
+ if (cdata->status != CONNECTED)
632
+ return Qnil;
633
+
634
+ // NOTE: Same as above.
635
+ if (uv_tcp_getpeername(cdata->_uv_handle, (struct sockaddr*)&peer_addr, &len))
636
+ return Qnil;
637
+
638
+ return ae_ip_utils_get_ip_port(&peer_addr, cdata->ip_type);
639
+ }
640
+
641
+
642
+ /** TCPSocket#set_connect_timeout() method. */
643
+
644
+ static
645
+ VALUE AsyncEngineTcpSocket_set_connect_timeout(VALUE self, VALUE _rb_timeout)
646
+ {
647
+ AE_TRACE2();
648
+
649
+ long delay;
650
+
651
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
652
+
653
+ // Return nil if the socket already connected or the connect timeout already set.
654
+ if (cdata->status == CONNECTED || cdata->_uv_timer_connect_timeout)
655
+ return Qnil;
656
+
657
+ delay = (long)(NUM2DBL(_rb_timeout) * 1000);
658
+ if (delay < 1)
659
+ delay = 1;
660
+
661
+ // Init and run the connect timeout timer.
662
+ cdata->_uv_timer_connect_timeout = ALLOC(uv_timer_t);
663
+ uv_timer_init(AE_uv_loop, cdata->_uv_timer_connect_timeout);
664
+ cdata->_uv_timer_connect_timeout->data = (void*)cdata;
665
+
666
+ if (uv_timer_start(cdata->_uv_timer_connect_timeout, _uv_timer_connect_timeout_callback, delay, 0)) {
667
+ if (cdata->_uv_timer_connect_timeout)
668
+ _ae_cancel_timer_connect_timeout(cdata);
669
+ ae_raise_last_uv_error();
670
+ }
671
+
672
+ return Qtrue;
673
+ }
674
+
675
+
676
+ static
677
+ void _uv_timer_connect_timeout_callback(uv_timer_t* handle, int status)
678
+ {
679
+ AE_TRACE2();
680
+
681
+ struct_cdata* cdata = (struct_cdata*)handle->data;
682
+
683
+ AE_ASSERT(cdata->_uv_timer_connect_timeout != NULL);
684
+ _ae_cancel_timer_connect_timeout(cdata);
685
+
686
+ cdata->flags |= CONNECT_TIMEOUT;
687
+
688
+ AE_CLOSE_UV_HANDLE(cdata->_uv_handle);
689
+ cdata->_uv_handle = NULL;
690
+ }
691
+
692
+
693
+ static
694
+ void _ae_cancel_timer_connect_timeout(struct_cdata* cdata)
695
+ {
696
+ AE_TRACE2();
697
+
698
+ AE_ASSERT(cdata->_uv_timer_connect_timeout != NULL);
699
+
700
+ AE_CLOSE_UV_HANDLE(cdata->_uv_timer_connect_timeout);
701
+ cdata->_uv_timer_connect_timeout = NULL;
702
+ }
703
+
704
+
705
+ /** TCPSocket#status() method. */
706
+
707
+ static
708
+ VALUE AsyncEngineTcpSocket_status(VALUE self)
709
+ {
710
+ AE_TRACE();
711
+
712
+ GET_CDATA_FROM_SELF;
713
+
714
+ if (! cdata->_uv_handle)
715
+ return symbol_closed;
716
+
717
+ switch(cdata->status) {
718
+ case CONNECTING:
719
+ return symbol_connecting;
720
+ break;
721
+ case CONNECTED:
722
+ return symbol_connected;
723
+ break;
724
+ }
725
+ }
726
+
727
+
728
+ /** TCPSocket#connected?() method. */
729
+
730
+ static
731
+ VALUE AsyncEngineTcpSocket_is_connected(VALUE self)
732
+ {
733
+ AE_TRACE();
734
+
735
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
736
+
737
+ if (cdata->status == CONNECTED)
738
+ return Qtrue;
739
+ else
740
+ return Qfalse;
741
+ }
742
+
743
+
744
+ /** TCPSocket#alive?() method. */
745
+
746
+ static
747
+ VALUE AsyncEngineTcpSocket_is_alive(VALUE self)
748
+ {
749
+ AE_TRACE();
750
+
751
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
752
+
753
+ return Qtrue;
754
+ }
755
+
756
+
757
+ /** TCPSocket#close() method. */
758
+
759
+ static
760
+ VALUE AsyncEngineTcpSocket_close(VALUE self)
761
+ {
762
+ AE_TRACE2();
763
+
764
+ VALUE error = Qnil;
765
+
766
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
767
+
768
+ AE_CLOSE_UV_HANDLE(cdata->_uv_handle);
769
+ cdata->_uv_handle = NULL;
770
+
771
+ if (cdata->_uv_timer_connect_timeout)
772
+ _ae_cancel_timer_connect_timeout(cdata);
773
+
774
+ // If it's connected we must manually call to on_disconnected() callback.
775
+ if (cdata->status == CONNECTED) {
776
+ ae_remove_handle(cdata->ae_handle_id);
777
+ // NOTE: This should be safe. Ruby wont GC the handle here since the object is
778
+ // in the stack.
779
+ // TODO: Run this with ae_run_with_error_handler() !
780
+ rb_funcall2(cdata->ae_handle, method_on_disconnected, 1, &error);
781
+ }
782
+
783
+ return Qtrue;
784
+ }
785
+
786
+
787
+ /** TCPSocket#close_gracefully() method. */
788
+
789
+ static
790
+ VALUE AsyncEngineTcpSocket_close_gracefully(int argc, VALUE *argv, VALUE self)
791
+ {
792
+ AE_TRACE2();
793
+
794
+ uv_shutdown_t *shutdown_req;
795
+
796
+ AE_RB_CHECK_NUM_ARGS(0,1);
797
+
798
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
799
+
800
+ // Avoid this method to be called twice.
801
+ if (cdata->flags & SHUTDOWN_REQUESTED)
802
+ return Qfalse;
803
+
804
+ // NOTE: If the handle is not connected yet, the uv_shutdown will occur after connection
805
+ // is done (or failed). If it fails, _uv_shutdown_callback() detects it and will not
806
+ // close the UV handle again, neither it will go to Ruby land.
807
+ if (cdata->status == CONNECTED) {
808
+ AE_ASSERT(! uv_read_stop((uv_stream_t*)cdata->_uv_handle));
809
+ shutdown_req = ALLOC(uv_shutdown_t);
810
+ if (uv_shutdown(shutdown_req, (uv_stream_t*)cdata->_uv_handle, _uv_shutdown_callback)) {
811
+ xfree(shutdown_req);
812
+ ae_raise_last_uv_error();
813
+ }
814
+ }
815
+
816
+ cdata->flags |= SHUTDOWN_REQUESTED;
817
+
818
+ return Qtrue;
819
+ }
820
+
821
+
822
+ static
823
+ void _uv_shutdown_callback(uv_shutdown_t* req, int status)
824
+ {
825
+ AE_TRACE2();
826
+
827
+ uv_tcp_t* _uv_handle = (uv_tcp_t*)req->handle;
828
+ struct_cdata* cdata = (struct_cdata*)_uv_handle->data;
829
+
830
+ xfree(req);
831
+
832
+ /*
833
+ * Check that the handle has not been closed between the call to #close_gracefully() and
834
+ * its callback. It occurs in case #close_gracefully() was called while in connecting
835
+ * state and finally a connection error (or timeout) ocurred, so the connect_cb() was
836
+ * called with error status and therefore the handle closed there.
837
+ */
838
+ if (cdata->_uv_handle && ! uv_is_closing((const uv_handle_t*)cdata->_uv_handle)) {
839
+ AE_CLOSE_UV_HANDLE(cdata->_uv_handle);
840
+ cdata->_uv_handle = NULL;
841
+ }
842
+ else
843
+ return;
844
+
845
+ last_uv_shutdown_callback_data.cdata = cdata;
846
+ ae_take_gvl_and_run_with_error_handler(_ae_shutdown_callback);
847
+ }
848
+
849
+
850
+ static
851
+ VALUE _ae_shutdown_callback(void)
852
+ {
853
+ AE_TRACE2();
854
+
855
+ struct_cdata* cdata = last_uv_shutdown_callback_data.cdata;
856
+ VALUE error;
857
+
858
+ ae_remove_handle(cdata->ae_handle_id);
859
+
860
+ /*
861
+ * We must manually call to on_disconnected() callback.
862
+ * If the shutdown was requested after receiving EOF from the peer
863
+ * then give EOF error to the on_disconnected() callback.
864
+ */
865
+ if (cdata->flags & EOF_RECEIVED) {
866
+ error = ae_get_uv_error(UV_EOF);
867
+ rb_funcall2(cdata->ae_handle, method_on_disconnected, 1, &error);
868
+ }
869
+ else {
870
+ error = Qnil;
871
+ rb_funcall2(cdata->ae_handle, method_on_disconnected, 1, &error);
872
+ }
873
+
874
+ return Qnil;
875
+ }
876
+
877
+
878
+ /**
879
+ * TCPSocket#destroy() private method.
880
+ *
881
+ * This method is safely called by AsyncEngine_release() by
882
+ * capturing and ignoring any exception/error.
883
+ */
884
+
885
+ static
886
+ VALUE AsyncEngineTcpSocket_destroy(VALUE self)
887
+ {
888
+ AE_TRACE2();
889
+
890
+ VALUE error = Qnil;
891
+
892
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
893
+
894
+ AE_CLOSE_UV_HANDLE(cdata->_uv_handle);
895
+ cdata->_uv_handle = NULL;
896
+
897
+ if (cdata->_uv_timer_connect_timeout)
898
+ _ae_cancel_timer_connect_timeout(cdata);
899
+
900
+ cdata->flags |= RELEASING;
901
+
902
+ if (cdata->status == CONNECTED) {
903
+ ae_remove_handle(cdata->ae_handle_id);
904
+ rb_funcall2(cdata->ae_handle, method_on_disconnected, 1, &error);
905
+ }
906
+
907
+ return Qtrue;
908
+ }