From d99591ca7bfbb4ec6c0b66ff86852439df4df68c Mon Sep 17 00:00:00 2001 From: Stanislav Sedov Date: Mon, 23 Feb 2009 00:41:07 +0000 Subject: - Ruby socket connect code seems to work unstably in case if connection to remote host was refused. FreeBSD connect(2) call returns EINVAL in that case and clears the error code, so there's no way to determine what happened. Reimplement ruby_connect via select call instead of polling the status by connect(2). This may also reduce overhead (though, not verified). Reported by: Saku Ytti --- lang/ruby18/files/patch-ext_socket_socket.c | 105 ++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 lang/ruby18/files/patch-ext_socket_socket.c (limited to 'lang/ruby18/files') diff --git a/lang/ruby18/files/patch-ext_socket_socket.c b/lang/ruby18/files/patch-ext_socket_socket.c new file mode 100644 index 000000000000..86092a459d58 --- /dev/null +++ b/lang/ruby18/files/patch-ext_socket_socket.c @@ -0,0 +1,105 @@ +--- ext/socket/socket.c.orig 2009-02-23 00:54:12.000000000 +0300 ++++ ext/socket/socket.c 2009-02-23 01:27:13.000000000 +0300 +@@ -1111,81 +1111,33 @@ + fcntl(fd, F_SETFL, mode|NONBLOCKING); + #endif /* HAVE_FCNTL */ + +- for (;;) { + #if defined(SOCKS) && !defined(SOCKS5) +- if (socks) { +- status = Rconnect(fd, sockaddr, len); +- } +- else +-#endif +- { +- status = connect(fd, sockaddr, len); +- } +- if (status < 0) { +- switch (errno) { +- case EAGAIN: +-#ifdef EINPROGRESS +- case EINPROGRESS: +-#endif +-#if WAIT_IN_PROGRESS > 0 +- sockerrlen = sizeof(sockerr); +- status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen); +- if (status) break; +- if (sockerr) { +- status = -1; +- errno = sockerr; +- break; +- } +-#endif +-#ifdef EALREADY +- case EALREADY: +-#endif +-#if WAIT_IN_PROGRESS > 0 +- wait_in_progress = WAIT_IN_PROGRESS; +-#endif +- status = wait_connectable(fd); +- if (status) { +- break; +- } +- errno = 0; +- continue; +- +-#if WAIT_IN_PROGRESS > 0 +- case EINVAL: +- if (wait_in_progress-- > 0) { +- /* +- * connect() after EINPROGRESS returns EINVAL on +- * some platforms, need to check true error +- * status. +- */ +- sockerrlen = sizeof(sockerr); +- status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen); +- if (!status && !sockerr) { +- struct timeval tv = {0, 100000}; +- rb_thread_wait_for(tv); +- continue; +- } +- status = -1; +- errno = sockerr; +- } +- break; +-#endif +- +-#ifdef EISCONN +- case EISCONN: +- status = 0; +- errno = 0; +- break; ++ if (socks) { ++ status = Rconnect(fd, sockaddr, len); ++ } ++ else + #endif +- default: +- break; ++ { ++ status = connect(fd, sockaddr, len); ++ } ++ ++ if (status < 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) { ++ status = wait_connectable(fd); ++ if (status == 0) { ++ int buf; ++ char c; ++ int len = sizeof(buf); ++ status = getpeername(fd, (struct sockaddr *)&buf, &len); ++ if (status == -1) { ++ read(fd, &c, 1); /* set errno. */ + } + } ++ } ++ + #ifdef HAVE_FCNTL +- fcntl(fd, F_SETFL, mode); ++ fcntl(fd, F_SETFL, mode); + #endif +- return status; +- } ++ return status; + } + + struct inetsock_arg -- cgit v1.2.3