Issue #23618: Refactor internal_connect()

On Windows, internal_connect() now reuses internal_connect_select() and always
calls getsockopt().
This commit is contained in:
Victor Stinner 2015-03-31 16:35:35 +02:00
parent dd88d3db45
commit b6c15bcad3
1 changed files with 30 additions and 79 deletions

View File

@ -2299,7 +2299,8 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args)
if (PyArg_ParseTuple(args, "iii:setsockopt",
&level, &optname, &flag)) {
res = setsockopt(s->sock_fd, level, optname, &flag, sizeof flag);
res = setsockopt(s->sock_fd, level, optname,
(char*)&flag, sizeof flag);
}
else {
PyErr_Clear();
@ -2450,7 +2451,17 @@ static int
internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
int *timeoutp)
{
int err, res, timeout;
#ifdef MS_WINDOWS
# define GET_ERROR WSAGetLastError()
# define IN_PROGRESS_ERR WSAEWOULDBLOCK
# define TIMEOUT_ERR WSAEWOULDBLOCK
#else
# define GET_ERROR errno
# define IN_PROGRESS_ERR EINPROGRESS
# define TIMEOUT_ERR EWOULDBLOCK
#endif
int res, err, in_progress, timeout;
timeout = 0;
@ -2458,105 +2469,45 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
#ifdef MS_WINDOWS
if (res < 0)
err = WSAGetLastError();
err = GET_ERROR;
else
err = res;
in_progress = (err == IN_PROGRESS_ERR);
if (s->sock_timeout > 0 && err == WSAEWOULDBLOCK && IS_SELECTABLE(s)) {
/* This is a mess. Best solution: trust select */
fd_set fds;
fd_set fds_exc;
struct timeval tv;
int conv;
_PyTime_AsTimeval_noraise(s->sock_timeout, &tv, _PyTime_ROUND_CEILING);
Py_BEGIN_ALLOW_THREADS
FD_ZERO(&fds);
FD_SET(s->sock_fd, &fds);
FD_ZERO(&fds_exc);
FD_SET(s->sock_fd, &fds_exc);
res = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
NULL, &fds, &fds_exc, &tv);
Py_END_ALLOW_THREADS
if (res == 0) {
err = WSAEWOULDBLOCK;
timeout = 1;
}
else if (res > 0) {
if (FD_ISSET(s->sock_fd, &fds)) {
/* The socket is in the writable set - this
means connected */
err = 0;
}
else {
/* As per MS docs, we need to call getsockopt()
to get the underlying error */
int res_size;
/* It must be in the exception set */
assert(FD_ISSET(s->sock_fd, &fds_exc));
res_size = sizeof res;
if (!getsockopt(s->sock_fd, SOL_SOCKET, SO_ERROR,
(char *)&res, &res_size)) {
err = res;
}
else {
err = WSAGetLastError();
}
}
}
else {
/* select() failed */
err = WSAGetLastError();
}
}
#else
if (res < 0)
err = errno;
else
err = 0;
if (s->sock_timeout > 0 && err == EINPROGRESS && IS_SELECTABLE(s)) {
if (s->sock_timeout > 0 && in_progress && IS_SELECTABLE(s)) {
timeout = internal_connect_select(s);
if (timeout == 0) {
/* Bug #1019808: in case of an EINPROGRESS,
use getsockopt(SO_ERROR) to get the real
error. */
if (timeout == 1) {
/* timed out */
err = TIMEOUT_ERR;
}
else if (timeout == 0) {
socklen_t res_size = sizeof res;
if (!getsockopt(s->sock_fd, SOL_SOCKET,
SO_ERROR, &res, &res_size)) {
if (!getsockopt(s->sock_fd, SOL_SOCKET, SO_ERROR,
(char*)&res, &res_size)) {
if (res == EISCONN)
res = 0;
err = res;
}
else {
/* getsockopt() failed */
err = errno;
err = GET_ERROR;
}
}
else if (timeout == -1) {
/* select failed */
err = errno;
}
else {
err = EWOULDBLOCK; /* timed out */
/* select() failed */
err = GET_ERROR;
}
}
#endif
*timeoutp = timeout;
assert(err >= 0);
return err;
#undef GET_ERROR
#undef IN_PROGRESS_ERR
#undef TIMEOUT_ERR
}
/* s.connect(sockaddr) method */