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.
- data/README.markdown +3 -0
- data/Rakefile +38 -0
- data/asyncengine.gemspec +8 -4
- data/ext/asyncengine/ae_call_from_other_thread.c +106 -0
- data/ext/asyncengine/ae_call_from_other_thread.h +12 -0
- data/ext/asyncengine/ae_handle_common.c +193 -48
- data/ext/asyncengine/ae_handle_common.h +40 -13
- data/ext/asyncengine/ae_ip_utils.c +246 -0
- data/ext/asyncengine/ae_ip_utils.h +25 -0
- data/ext/asyncengine/ae_next_tick.c +81 -21
- data/ext/asyncengine/ae_next_tick.h +4 -2
- data/ext/asyncengine/ae_resolver.c +156 -0
- data/ext/asyncengine/ae_resolver.h +10 -0
- data/ext/asyncengine/ae_tcp.c +908 -0
- data/ext/asyncengine/ae_tcp.h +20 -0
- data/ext/asyncengine/ae_timer.c +355 -81
- data/ext/asyncengine/ae_timer.h +11 -4
- data/ext/asyncengine/ae_udp.c +579 -13
- data/ext/asyncengine/ae_udp.h +15 -2
- data/ext/asyncengine/ae_utils.c +192 -0
- data/ext/asyncengine/ae_utils.h +16 -0
- data/ext/asyncengine/asyncengine_ruby.c +469 -26
- data/ext/asyncengine/asyncengine_ruby.h +49 -11
- data/ext/asyncengine/debug.h +68 -0
- data/ext/asyncengine/extconf.rb +26 -2
- data/ext/asyncengine/ip_parser.c +5954 -0
- data/ext/asyncengine/ip_parser.h +16 -0
- data/ext/asyncengine/libuv/AUTHORS +16 -0
- data/ext/asyncengine/libuv/common.gypi +4 -4
- data/ext/asyncengine/libuv/config-mingw.mk +6 -6
- data/ext/asyncengine/libuv/config-unix.mk +13 -13
- data/ext/asyncengine/libuv/gyp_uv +5 -1
- data/ext/asyncengine/libuv/ibc_tests/exec_test.sh +8 -0
- data/ext/asyncengine/libuv/ibc_tests/uv_shutdown_write_issue.c +171 -0
- data/ext/asyncengine/libuv/ibc_tests/uv_tcp_close_while_connecting.c +102 -0
- data/ext/asyncengine/libuv/include/uv-private/ngx-queue.h +3 -1
- data/ext/asyncengine/libuv/include/uv-private/uv-unix.h +103 -50
- data/ext/asyncengine/libuv/include/uv-private/uv-win.h +76 -24
- data/ext/asyncengine/libuv/include/uv.h +353 -88
- data/ext/asyncengine/libuv/src/ares/ares__close_sockets.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares__get_hostent.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares__read_line.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares__timeval.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_cancel.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_data.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_destroy.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_expand_name.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_expand_string.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_fds.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_free_hostent.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_free_string.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_gethostbyaddr.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_gethostbyname.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_getnameinfo.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_getopt.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_getsock.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_init.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_library_init.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_llist.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_mkquery.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_nowarn.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_options.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_a_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_aaaa_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_mx_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_ns_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_ptr_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_srv_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_parse_txt_reply.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_process.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_query.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_search.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_send.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_strcasecmp.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_strdup.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_strerror.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_timeout.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_version.o +0 -0
- data/ext/asyncengine/libuv/src/ares/ares_writev.o +0 -0
- data/ext/asyncengine/libuv/src/ares/bitncmp.o +0 -0
- data/ext/asyncengine/libuv/src/ares/inet_net_pton.o +0 -0
- data/ext/asyncengine/libuv/src/ares/inet_ntop.o +0 -0
- data/ext/asyncengine/libuv/src/cares.c +225 -0
- data/ext/asyncengine/libuv/src/cares.o +0 -0
- data/ext/asyncengine/libuv/src/fs-poll.c +237 -0
- data/ext/asyncengine/libuv/src/fs-poll.o +0 -0
- data/ext/asyncengine/libuv/src/unix/async.c +78 -17
- data/ext/asyncengine/libuv/src/unix/async.o +0 -0
- data/ext/asyncengine/libuv/src/unix/core.c +305 -213
- data/ext/asyncengine/libuv/src/unix/core.o +0 -0
- data/ext/asyncengine/libuv/src/unix/cygwin.c +1 -1
- data/ext/asyncengine/libuv/src/unix/darwin.c +2 -1
- data/ext/asyncengine/libuv/src/unix/dl.c +36 -44
- data/ext/asyncengine/libuv/src/unix/dl.o +0 -0
- data/ext/asyncengine/libuv/src/unix/eio/eio.o +0 -0
- data/ext/asyncengine/libuv/src/unix/error.c +6 -0
- data/ext/asyncengine/libuv/src/unix/error.o +0 -0
- data/ext/asyncengine/libuv/src/unix/ev/ev.c +8 -4
- data/ext/asyncengine/libuv/src/unix/ev/ev.o +0 -0
- data/ext/asyncengine/libuv/src/unix/freebsd.c +1 -1
- data/ext/asyncengine/libuv/src/unix/fs.c +25 -33
- data/ext/asyncengine/libuv/src/unix/fs.o +0 -0
- data/ext/asyncengine/libuv/src/unix/internal.h +50 -31
- data/ext/asyncengine/libuv/src/unix/kqueue.c +2 -7
- data/ext/asyncengine/libuv/src/unix/linux/core.o +0 -0
- data/ext/asyncengine/libuv/src/unix/linux/inotify.c +12 -14
- data/ext/asyncengine/libuv/src/unix/linux/inotify.o +0 -0
- data/ext/asyncengine/libuv/src/unix/linux/{core.c → linux-core.c} +1 -1
- data/ext/asyncengine/libuv/src/unix/linux/linux-core.o +0 -0
- data/ext/asyncengine/libuv/src/unix/linux/syscalls.c +147 -1
- data/ext/asyncengine/libuv/src/unix/linux/syscalls.h +39 -2
- data/ext/asyncengine/libuv/src/unix/linux/syscalls.o +0 -0
- data/ext/asyncengine/libuv/src/unix/loop-watcher.c +63 -0
- data/ext/asyncengine/libuv/src/unix/loop-watcher.o +0 -0
- data/ext/asyncengine/libuv/src/unix/loop.c +29 -6
- data/ext/asyncengine/libuv/src/unix/loop.o +0 -0
- data/ext/asyncengine/libuv/src/unix/netbsd.c +1 -1
- data/ext/asyncengine/libuv/src/unix/openbsd.c +1 -1
- data/ext/asyncengine/libuv/src/unix/pipe.c +31 -36
- data/ext/asyncengine/libuv/src/unix/pipe.o +0 -0
- data/ext/asyncengine/libuv/src/unix/poll.c +116 -0
- data/ext/asyncengine/libuv/src/unix/poll.o +0 -0
- data/ext/asyncengine/libuv/src/unix/process.c +193 -115
- data/ext/asyncengine/libuv/src/unix/process.o +0 -0
- data/ext/asyncengine/libuv/src/unix/stream.c +146 -153
- data/ext/asyncengine/libuv/src/unix/stream.o +0 -0
- data/ext/asyncengine/libuv/src/unix/sunos.c +45 -36
- data/ext/asyncengine/libuv/src/unix/tcp.c +6 -5
- data/ext/asyncengine/libuv/src/unix/tcp.o +0 -0
- data/ext/asyncengine/libuv/src/unix/thread.c +82 -25
- data/ext/asyncengine/libuv/src/unix/thread.o +0 -0
- data/ext/asyncengine/libuv/src/unix/timer.c +69 -58
- data/ext/asyncengine/libuv/src/unix/timer.o +0 -0
- data/ext/asyncengine/libuv/src/unix/tty.c +3 -3
- data/ext/asyncengine/libuv/src/unix/tty.o +0 -0
- data/ext/asyncengine/libuv/src/unix/udp.c +57 -66
- data/ext/asyncengine/libuv/src/unix/udp.o +0 -0
- data/ext/asyncengine/libuv/src/unix/uv-eio.c +33 -50
- data/ext/asyncengine/libuv/src/unix/uv-eio.o +0 -0
- data/ext/asyncengine/libuv/src/uv-common.c +68 -38
- data/ext/asyncengine/libuv/src/uv-common.h +104 -20
- data/ext/asyncengine/libuv/src/uv-common.o +0 -0
- data/ext/asyncengine/libuv/src/win/async.c +20 -17
- data/ext/asyncengine/libuv/src/win/core.c +44 -31
- data/ext/asyncengine/libuv/src/win/dl.c +40 -36
- data/ext/asyncengine/libuv/src/win/error.c +21 -1
- data/ext/asyncengine/libuv/src/win/fs-event.c +19 -21
- data/ext/asyncengine/libuv/src/win/fs.c +541 -189
- data/ext/asyncengine/libuv/src/win/getaddrinfo.c +56 -63
- data/ext/asyncengine/libuv/src/win/handle-inl.h +145 -0
- data/ext/asyncengine/libuv/src/win/handle.c +26 -101
- data/ext/asyncengine/libuv/src/win/internal.h +92 -107
- data/ext/asyncengine/libuv/src/win/loop-watcher.c +6 -14
- data/ext/asyncengine/libuv/src/win/pipe.c +78 -64
- data/ext/asyncengine/libuv/src/win/poll.c +618 -0
- data/ext/asyncengine/libuv/src/win/process-stdio.c +479 -0
- data/ext/asyncengine/libuv/src/win/process.c +147 -274
- data/ext/asyncengine/libuv/src/win/req-inl.h +225 -0
- data/ext/asyncengine/libuv/src/win/req.c +0 -149
- data/ext/asyncengine/libuv/src/{unix/check.c → win/stream-inl.h} +31 -42
- data/ext/asyncengine/libuv/src/win/stream.c +9 -43
- data/ext/asyncengine/libuv/src/win/tcp.c +200 -82
- data/ext/asyncengine/libuv/src/win/thread.c +42 -2
- data/ext/asyncengine/libuv/src/win/threadpool.c +3 -2
- data/ext/asyncengine/libuv/src/win/timer.c +13 -63
- data/ext/asyncengine/libuv/src/win/tty.c +26 -20
- data/ext/asyncengine/libuv/src/win/udp.c +26 -17
- data/ext/asyncengine/libuv/src/win/util.c +312 -167
- data/ext/asyncengine/libuv/src/win/winapi.c +16 -1
- data/ext/asyncengine/libuv/src/win/winapi.h +33 -9
- data/ext/asyncengine/libuv/src/win/winsock.c +88 -1
- data/ext/asyncengine/libuv/src/win/winsock.h +36 -3
- data/ext/asyncengine/libuv/test/benchmark-ares.c +16 -17
- data/ext/asyncengine/libuv/test/benchmark-fs-stat.c +164 -0
- data/ext/asyncengine/libuv/test/benchmark-list.h +9 -0
- data/ext/asyncengine/libuv/{src/unix/prepare.c → test/benchmark-loop-count.c} +42 -33
- data/ext/asyncengine/libuv/test/benchmark-million-timers.c +65 -0
- data/ext/asyncengine/libuv/test/benchmark-pound.c +1 -1
- data/ext/asyncengine/libuv/test/benchmark-sizes.c +2 -0
- data/ext/asyncengine/libuv/test/benchmark-spawn.c +7 -1
- data/ext/asyncengine/libuv/test/benchmark-udp-packet-storm.c +1 -1
- data/ext/asyncengine/libuv/test/echo-server.c +8 -0
- data/ext/asyncengine/libuv/test/run-tests.c +30 -0
- data/ext/asyncengine/libuv/test/runner-unix.c +6 -26
- data/ext/asyncengine/libuv/test/runner-win.c +5 -63
- data/ext/asyncengine/libuv/test/runner.c +10 -1
- data/ext/asyncengine/libuv/test/task.h +0 -8
- data/ext/asyncengine/libuv/test/test-async.c +43 -141
- data/ext/asyncengine/libuv/test/test-callback-order.c +76 -0
- data/ext/asyncengine/libuv/test/test-counters-init.c +2 -3
- data/ext/asyncengine/libuv/test/test-dlerror.c +17 -8
- data/ext/asyncengine/libuv/test/test-fs-event.c +31 -39
- data/ext/asyncengine/libuv/test/test-fs-poll.c +146 -0
- data/ext/asyncengine/libuv/test/test-fs.c +114 -2
- data/ext/asyncengine/libuv/test/test-gethostbyname.c +8 -8
- data/ext/asyncengine/libuv/test/test-hrtime.c +18 -15
- data/ext/asyncengine/libuv/test/test-ipc.c +8 -2
- data/ext/asyncengine/libuv/test/test-list.h +59 -9
- data/ext/asyncengine/libuv/test/test-loop-handles.c +2 -25
- data/ext/asyncengine/libuv/{src/unix/idle.c → test/test-poll-close.c} +37 -39
- data/ext/asyncengine/libuv/test/test-poll.c +573 -0
- data/ext/asyncengine/libuv/test/test-ref.c +79 -63
- data/ext/asyncengine/libuv/test/test-run-once.c +15 -11
- data/ext/asyncengine/libuv/test/test-semaphore.c +111 -0
- data/ext/asyncengine/libuv/test/test-spawn.c +368 -20
- data/ext/asyncengine/libuv/test/test-stdio-over-pipes.c +25 -35
- data/ext/asyncengine/libuv/test/test-tcp-close-while-connecting.c +80 -0
- data/ext/asyncengine/libuv/test/test-tcp-close.c +1 -1
- data/ext/asyncengine/libuv/test/test-tcp-connect-error-after-write.c +95 -0
- data/ext/asyncengine/libuv/test/test-tcp-connect-timeout.c +85 -0
- data/ext/asyncengine/libuv/test/test-tcp-shutdown-after-write.c +131 -0
- data/ext/asyncengine/libuv/test/test-tcp-write-error.c +2 -2
- data/ext/asyncengine/libuv/test/test-tcp-writealot.c +29 -54
- data/ext/asyncengine/libuv/test/test-timer-again.c +1 -1
- data/ext/asyncengine/libuv/test/test-timer.c +23 -1
- data/ext/asyncengine/libuv/test/test-udp-options.c +1 -1
- data/ext/asyncengine/libuv/test/{test-eio-overflow.c → test-walk-handles.c} +31 -44
- data/ext/asyncengine/libuv/uv.gyp +26 -9
- data/ext/asyncengine/rb_utilities.c +54 -0
- data/ext/asyncengine/rb_utilities.h +63 -0
- data/lib/asyncengine.rb +45 -38
- data/lib/asyncengine/asyncengine_ext.so +0 -0
- data/lib/asyncengine/debug.rb +37 -0
- data/lib/asyncengine/handle.rb +9 -0
- data/lib/asyncengine/tcp.rb +28 -0
- data/lib/asyncengine/timer.rb +18 -28
- data/lib/asyncengine/udp.rb +29 -0
- data/lib/asyncengine/utils.rb +32 -0
- data/lib/asyncengine/uv_error.rb +17 -0
- data/lib/asyncengine/version.rb +9 -1
- data/test/ae_test_helper.rb +62 -0
- data/test/test_basic.rb +169 -0
- data/test/test_call_from_other_thread.rb +55 -0
- data/test/test_error.rb +92 -0
- data/test/test_ip_utils.rb +44 -0
- data/test/test_next_tick.rb +37 -0
- data/test/test_resolver.rb +51 -0
- data/test/test_threads.rb +69 -0
- data/test/test_timer.rb +95 -0
- data/test/test_udp.rb +216 -0
- data/test/test_utils.rb +49 -0
- metadata +84 -57
- data/ext/asyncengine/libuv/mkmf.log +0 -24
- data/ext/asyncengine/libuv/src/unix/cares.c +0 -194
- data/ext/asyncengine/libuv/src/unix/cares.o +0 -0
- data/ext/asyncengine/libuv/src/unix/check.o +0 -0
- data/ext/asyncengine/libuv/src/unix/idle.o +0 -0
- data/ext/asyncengine/libuv/src/unix/prepare.o +0 -0
- data/ext/asyncengine/libuv/src/win/cares.c +0 -290
- data/lib/asyncengine/errors.rb +0 -5
- 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
|
|
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,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
|
+
}
|