asyncengine 0.0.1.testing1 → 0.0.2.alpha1

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