Issue #23618: Refactor internal_connect()
On Windows, internal_connect() now reuses internal_connect_select() and always calls getsockopt().
This commit is contained in:
parent
dd88d3db45
commit
b6c15bcad3
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue