ext/kgio/accept.c in kgio-2.3.3 vs ext/kgio/accept.c in kgio-2.4.0
- old
+ new
@@ -14,12 +14,15 @@
static int accept4_flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
#endif /* ! linux */
struct accept_args {
int fd;
+ int flags;
struct sockaddr *addr;
socklen_t *addrlen;
+ VALUE accept_io;
+ VALUE accepted_class;
};
/*
* Sets the default class for newly accepted sockets. This is
* legacy behavior, kgio_accept and kgio_tryaccept now take optional
@@ -51,19 +54,23 @@
static VALUE get_accepted(VALUE klass)
{
return cClientSocket;
}
+/*
+ * accept() wrapper that'll fall back on accept() if we were built on
+ * a system with accept4() but run on a system without accept4()
+ */
static VALUE xaccept(void *ptr)
{
struct accept_args *a = ptr;
int rv;
- rv = accept_fn(a->fd, a->addr, a->addrlen, accept4_flags);
+ rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags);
if (rv == -1 && errno == ENOSYS && accept_fn != my_accept4) {
accept_fn = my_accept4;
- rv = accept_fn(a->fd, a->addr, a->addrlen, accept4_flags);
+ rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags);
}
return (VALUE)rv;
}
@@ -122,100 +129,118 @@
return rv;
}
#define set_blocking_or_block(fd) (void)rb_io_wait_readable(fd)
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
-static VALUE acceptor(int argc, const VALUE *argv)
+static void
+prepare_accept(struct accept_args *a, VALUE self, int argc, const VALUE *argv)
{
- if (argc == 0)
- return cClientSocket; /* default, legacy behavior */
- else if (argc == 1)
- return argv[0];
+ a->fd = my_fileno(self);
+ a->accept_io = self;
+ switch (argc) {
+ case 2:
+ a->flags = NUM2INT(argv[1]);
+ a->accepted_class = NIL_P(argv[0]) ? cClientSocket : argv[0];
+ return;
+ case 0: /* default, legacy behavior */
+ a->flags = accept4_flags;
+ a->accepted_class = cClientSocket;
+ return;
+ case 1:
+ a->flags = accept4_flags;
+ a->accepted_class = NIL_P(argv[0]) ? cClientSocket : argv[0];
+ return;
+ }
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
}
+static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
+{
+ VALUE host;
+ int host_len, rc;
+ char *host_ptr;
+
+ switch (addr->ss_family) {
+ case AF_INET:
+ host_len = (long)INET_ADDRSTRLEN;
+ break;
+ case AF_INET6:
+ host_len = (long)INET6_ADDRSTRLEN;
+ break;
+ default:
+ rb_raise(rb_eRuntimeError, "unsupported address family");
+ }
+ host = rb_str_new(NULL, host_len);
+ host_ptr = RSTRING_PTR(host);
+ rc = getnameinfo((struct sockaddr *)addr, len,
+ host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
+ if (rc != 0)
+ rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
+ rb_str_set_len(host, strlen(host_ptr));
+ return rb_ivar_set(io, iv_kgio_addr, host);
+}
+
#if defined(__linux__)
# define post_accept kgio_autopush_accept
#else
# define post_accept(a,b) for(;0;)
#endif
static VALUE
-my_accept(VALUE accept_io, VALUE klass,
- struct sockaddr *addr, socklen_t *addrlen, int nonblock)
+my_accept(struct accept_args *a, int force_nonblock)
{
- int client;
+ int client_fd;
VALUE client_io;
- struct accept_args a;
+ int retried = 0;
- a.fd = my_fileno(accept_io);
- a.addr = addr;
- a.addrlen = addrlen;
retry:
- client = thread_accept(&a, nonblock);
- if (client == -1) {
+ client_fd = thread_accept(a, force_nonblock);
+ if (client_fd == -1) {
switch (errno) {
case EAGAIN:
- if (nonblock)
+ if (force_nonblock)
return Qnil;
- set_blocking_or_block(a.fd);
+ a->fd = my_fileno(a->accept_io);
+ set_blocking_or_block(a->fd);
#ifdef ECONNABORTED
case ECONNABORTED:
#endif /* ECONNABORTED */
#ifdef EPROTO
case EPROTO:
#endif /* EPROTO */
case EINTR:
+ a->fd = my_fileno(a->accept_io);
goto retry;
case ENOMEM:
case EMFILE:
case ENFILE:
#ifdef ENOBUFS
case ENOBUFS:
#endif /* ENOBUFS */
- errno = 0;
- rb_gc();
- client = thread_accept(&a, nonblock);
- }
- if (client == -1) {
- if (errno == EINTR)
+ if (!retried) {
+ retried = 1;
+ errno = 0;
+ rb_gc();
goto retry;
+ }
+ default:
rb_sys_fail("accept");
}
}
- client_io = sock_for_fd(klass, client);
- post_accept(accept_io, client_io);
+ client_io = sock_for_fd(a->accepted_class, client_fd);
+ post_accept(a->accept_io, client_io);
+
+ if (a->addr)
+ in_addr_set(client_io,
+ (struct sockaddr_storage *)a->addr, *a->addrlen);
+ else
+ rb_ivar_set(client_io, iv_kgio_addr, localhost);
return client_io;
}
-static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
-{
- VALUE host;
- int host_len, rc;
- char *host_ptr;
-
- switch (addr->ss_family) {
- case AF_INET:
- host_len = (long)INET_ADDRSTRLEN;
- break;
- case AF_INET6:
- host_len = (long)INET6_ADDRSTRLEN;
- break;
- default:
- rb_raise(rb_eRuntimeError, "unsupported address family");
- }
- host = rb_str_new(NULL, host_len);
- host_ptr = RSTRING_PTR(host);
- rc = getnameinfo((struct sockaddr *)addr, len,
- host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
- if (rc != 0)
- rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
- rb_str_set_len(host, strlen(host_ptr));
- return rb_ivar_set(io, iv_kgio_addr, host);
-}
-
/*
* call-seq:
*
* io.kgio_addr! => refreshes the given sock address
*/
@@ -237,115 +262,143 @@
/*
* call-seq:
*
* server = Kgio::TCPServer.new('0.0.0.0', 80)
* server.kgio_tryaccept -> Kgio::Socket or nil
+ * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil
+ * server.kgio_tryaccept(nil, flags) -> Kgio::Socket or nil
*
* Initiates a non-blocking accept and returns a generic Kgio::Socket
* object with the kgio_addr attribute set to the IP address of the
* connected client on success.
*
* Returns nil on EAGAIN, and raises on other errors.
*
- * An optional class argument may be specified to override the
- * Kgio::Socket-class return value:
+ * An optional +klass+ argument may be specified to override the
+ * Kgio::Socket-class on a successful return value.
*
- * server.kgio_tryaccept(MySocket) -> MySocket
+ * An optional +flags+ argument may also be specifed to override the
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
+ * is a bitmask that may contain any combination of:
+ *
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
+ * - IO::NONBLOCK - non-blocking flag
*/
-static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
+static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE self)
{
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
- VALUE klass = acceptor(argc, argv);
- VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 1);
+ struct accept_args a;
- if (!NIL_P(rv))
- in_addr_set(rv, &addr, addrlen);
- return rv;
+ a.addr = (struct sockaddr *)&addr;
+ a.addrlen = &addrlen;
+ prepare_accept(&a, self, argc, argv);
+ return my_accept(&a, 1);
}
/*
* call-seq:
*
* server = Kgio::TCPServer.new('0.0.0.0', 80)
* server.kgio_accept -> Kgio::Socket or nil
+ * server.kgio_tryaccept -> Kgio::Socket or nil
+ * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil
*
* Initiates a blocking accept and returns a generic Kgio::Socket
* object with the kgio_addr attribute set to the IP address of
* the client on success.
*
* On Ruby implementations using native threads, this can use a blocking
* accept(2) (or accept4(2)) system call to avoid thundering herds.
*
- * An optional class argument may be specified to override the
- * Kgio::Socket-class return value:
+ * An optional +klass+ argument may be specified to override the
+ * Kgio::Socket-class on a successful return value.
*
- * server.kgio_accept(MySocket) -> MySocket
+ * An optional +flags+ argument may also be specifed to override the
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
+ * is a bitmask that may contain any combination of:
+ *
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
+ * - IO::NONBLOCK - non-blocking flag
*/
-static VALUE tcp_accept(int argc, VALUE *argv, VALUE io)
+static VALUE tcp_accept(int argc, VALUE *argv, VALUE self)
{
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
- VALUE klass = acceptor(argc, argv);
- VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 0);
+ struct accept_args a;
- in_addr_set(rv, &addr, addrlen);
- return rv;
+ a.addr = (struct sockaddr *)&addr;
+ a.addrlen = &addrlen;
+ prepare_accept(&a, self, argc, argv);
+ return my_accept(&a, 0);
}
/*
* call-seq:
*
* server = Kgio::UNIXServer.new("/path/to/unix/socket")
* server.kgio_tryaccept -> Kgio::Socket or nil
+ * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil
+ * server.kgio_tryaccept(nil, flags) -> Kgio::Socket or nil
*
* Initiates a non-blocking accept and returns a generic Kgio::Socket
* object with the kgio_addr attribute set (to the value of
* Kgio::LOCALHOST) on success.
*
- * Returns nil on EAGAIN, and raises on other errors.
+ * An optional +klass+ argument may be specified to override the
+ * Kgio::Socket-class on a successful return value.
*
- * An optional class argument may be specified to override the
- * Kgio::Socket-class return value:
+ * An optional +flags+ argument may also be specifed to override the
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
+ * is a bitmask that may contain any combination of:
*
- * server.kgio_tryaccept(MySocket) -> MySocket
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
+ * - IO::NONBLOCK - non-blocking flag
*/
-static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE io)
+static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE self)
{
- VALUE klass = acceptor(argc, argv);
- VALUE rv = my_accept(io, klass, NULL, NULL, 1);
+ struct accept_args a;
- if (!NIL_P(rv))
- rb_ivar_set(rv, iv_kgio_addr, localhost);
- return rv;
+ a.addr = NULL;
+ a.addrlen = NULL;
+ prepare_accept(&a, self, argc, argv);
+ return my_accept(&a, 1);
}
/*
* call-seq:
*
* server = Kgio::UNIXServer.new("/path/to/unix/socket")
* server.kgio_accept -> Kgio::Socket or nil
+ * server.kgio_accept(klass = MySocket) -> MySocket or nil
+ * server.kgio_accept(nil, flags) -> Kgio::Socket or nil
*
* Initiates a blocking accept and returns a generic Kgio::Socket
* object with the kgio_addr attribute set (to the value of
* Kgio::LOCALHOST) on success.
*
* On Ruby implementations using native threads, this can use a blocking
* accept(2) (or accept4(2)) system call to avoid thundering herds.
*
- * An optional class argument may be specified to override the
- * Kgio::Socket-class return value:
+ * An optional +klass+ argument may be specified to override the
+ * Kgio::Socket-class on a successful return value.
*
- * server.kgio_accept(MySocket) -> MySocket
+ * An optional +flags+ argument may also be specifed to override the
+ * value of +Kgio.accept_cloexec+ and +Kgio.accept_nonblock+. +flags+
+ * is a bitmask that may contain any combination of:
+ *
+ * - Fcntl::FD_CLOEXEC - close-on-exec flag
+ * - IO::NONBLOCK - non-blocking flag
*/
-static VALUE unix_accept(int argc, VALUE *argv, VALUE io)
+static VALUE unix_accept(int argc, VALUE *argv, VALUE self)
{
- VALUE klass = acceptor(argc, argv);
- VALUE rv = my_accept(io, klass, NULL, NULL, 0);
+ struct accept_args a;
- rb_ivar_set(rv, iv_kgio_addr, localhost);
- return rv;
+ a.addr = NULL;
+ a.addrlen = NULL;
+ prepare_accept(&a, self, argc, argv);
+ return my_accept(&a, 0);
}
/*
* call-seq:
*
@@ -382,10 +435,10 @@
* Sets whether or not Kgio::Socket objects created by
* TCPServer#kgio_accept,
* TCPServer#kgio_tryaccept,
* UNIXServer#kgio_accept,
* and UNIXServer#kgio_tryaccept
- * are created with the FD_CLOEXEC file descriptor flag.
+ * default to being created with the FD_CLOEXEC file descriptor flag.
*
* This is on by default, as there is little reason to deal to enable
* it for client sockets on a socket server.
*/
static VALUE set_cloexec(VALUE mod, VALUE boolean)