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", if (PyArg_ParseTuple(args, "iii:setsockopt",
&level, &optname, &flag)) { &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 { else {
PyErr_Clear(); PyErr_Clear();
@ -2450,7 +2451,17 @@ static int
internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen, internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
int *timeoutp) 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; timeout = 0;
@ -2458,105 +2469,45 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
res = connect(s->sock_fd, addr, addrlen); res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
#ifdef MS_WINDOWS
if (res < 0) if (res < 0)
err = WSAGetLastError(); err = GET_ERROR;
else else
err = res; err = res;
in_progress = (err == IN_PROGRESS_ERR);
if (s->sock_timeout > 0 && err == WSAEWOULDBLOCK && IS_SELECTABLE(s)) { if (s->sock_timeout > 0 && in_progress && 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)) {
timeout = internal_connect_select(s); timeout = internal_connect_select(s);
if (timeout == 0) { if (timeout == 1) {
/* Bug #1019808: in case of an EINPROGRESS, /* timed out */
use getsockopt(SO_ERROR) to get the real err = TIMEOUT_ERR;
error. */ }
else if (timeout == 0) {
socklen_t res_size = sizeof res; socklen_t res_size = sizeof res;
if (!getsockopt(s->sock_fd, SOL_SOCKET, if (!getsockopt(s->sock_fd, SOL_SOCKET, SO_ERROR,
SO_ERROR, &res, &res_size)) { (char*)&res, &res_size)) {
if (res == EISCONN) if (res == EISCONN)
res = 0; res = 0;
err = res; err = res;
} }
else { else {
/* getsockopt() failed */ /* getsockopt() failed */
err = errno; err = GET_ERROR;
} }
} }
else if (timeout == -1) {
/* select failed */
err = errno;
}
else { else {
err = EWOULDBLOCK; /* timed out */ /* select() failed */
err = GET_ERROR;
} }
} }
#endif
*timeoutp = timeout; *timeoutp = timeout;
assert(err >= 0); assert(err >= 0);
return err; return err;
#undef GET_ERROR
#undef IN_PROGRESS_ERR
#undef TIMEOUT_ERR
} }
/* s.connect(sockaddr) method */ /* s.connect(sockaddr) method */