diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/socket/socket.c | 52 |
1 files changed, 45 insertions, 7 deletions
diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 97df7cf780..ac2a84b2a5 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -52,10 +52,16 @@ #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> +#endif +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> +#endif +#ifdef HAVE_FCNTL_H #include <fcntl.h> #endif +#endif #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif @@ -755,15 +761,39 @@ ruby_socket(domain, type, proto) return fd; } -static void -thread_write_select(fd) +static int +wait_connectable(fd) int fd; { - fd_set fds; + int sockerr, sockerrlen; + fd_set fds_w; + fd_set fds_e; - FD_ZERO(&fds); - FD_SET(fd, &fds); - rb_thread_select(fd+1, 0, &fds, 0, 0); + for (;;) { + FD_ZERO(&fds_w); + FD_ZERO(&fds_e); + + FD_SET(fd, &fds_w); + FD_SET(fd, &fds_e); + + rb_thread_select(fd+1, 0, &fds_w, &fds_e, 0); + + if (FD_ISSET(fd, &fds_w)) { + return 0; + } + else if (FD_ISSET(fd, &fds_e)) { + sockerrlen = sizeof(sockerr); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, + &sockerrlen) == 0) { + if (sockerr == 0) + continue; /* workaround for winsock */ + errno = sockerr; + } + return -1; + } + } + + return 0; } #ifdef __CYGWIN__ @@ -796,7 +826,11 @@ ruby_connect(fd, sockaddr, len, socks) #endif #if defined(HAVE_FCNTL) +# if defined(F_GETFL) mode = fcntl(fd, F_GETFL, 0); +# else + mode = 0; +# endif #ifdef O_NDELAY # define NONBLOCKING O_NDELAY @@ -845,7 +879,11 @@ ruby_connect(fd, sockaddr, len, socks) #if WAIT_IN_PROGRESS > 0 wait_in_progress = WAIT_IN_PROGRESS; #endif - thread_write_select(fd); + status = wait_connectable(fd); + if (status) { + break; + } + errno = 0; continue; #if WAIT_IN_PROGRESS > 0 |