David Phillips' FD_SETSIZE fix

This commit is contained in:
Daniel Stenberg 2004-11-19 08:52:33 +00:00
parent dcea109bb5
commit 1a05a90f1c
16 changed files with 467 additions and 179 deletions

View File

@ -6,6 +6,10 @@
Changelog Changelog
Daniel (18 November 2004)
- David Phillips fixed libcurl to not crash anymore when more than FD_SETSIZE
file descriptors are in use. Test case 518 added to verify.
Daniel (15 November 2004) Daniel (15 November 2004)
- To test my fix for the CURLINFO_REDIRECT_TIME bug, I added time_redirect and - To test my fix for the CURLINFO_REDIRECT_TIME bug, I added time_redirect and
num_redirects support to the -w writeout option for the command line tool. num_redirects support to the -w writeout option for the command line tool.
@ -178,7 +182,7 @@ Daniel (11 October 2004)
send() on other systems. Alan Pinstein verified the fix. send() on other systems. Alan Pinstein verified the fix.
Daniel (10 October 2004) Daniel (10 October 2004)
- Systems with 64bit longs no longeruse strtoll() or our strtoll- replacement - Systems with 64bit longs no longer use strtoll() or our strtoll- replacement
to parse 64 bit numbers. strtol() works fine. Added a configure check to to parse 64 bit numbers. strtol() works fine. Added a configure check to
detect if [constant]LL works and if so, use that in the strtoll replacement detect if [constant]LL works and if so, use that in the strtoll replacement
code to work around compiler warnings reported by Andy Cedilnik. code to work around compiler warnings reported by Andy Cedilnik.

View File

@ -19,6 +19,7 @@ This release includes the following changes:
This release includes the following bugfixes: This release includes the following bugfixes:
o now gracefully bails out when exceeding FD_SETSIZE file descriptors
o CURLINFO_REDIRECT_TIME works o CURLINFO_REDIRECT_TIME works
o building with gssapi libs and hdeaders in the default dirs o building with gssapi libs and hdeaders in the default dirs
o curl_getdate() parsing of dates later than year 2037 with 32 bit time_t o curl_getdate() parsing of dates later than year 2037 with 32 bit time_t
@ -43,6 +44,7 @@ advice from friends like these:
Peter Wullinger, Guillaume Arluison, Alexander Krasnostavsky, Mohun Biswas, Peter Wullinger, Guillaume Arluison, Alexander Krasnostavsky, Mohun Biswas,
Tomas Pospisek, Gisle Vanem, Dan Fandrich, Paul Nolan, Andres Garcia, Tomas Pospisek, Gisle Vanem, Dan Fandrich, Paul Nolan, Andres Garcia,
Tim Sneddon, Ian Gulliver, Jean-Philippe Barrette-LaPierre, Jeff Phillips Tim Sneddon, Ian Gulliver, Jean-Philippe Barrette-LaPierre, Jeff Phillips,
Wojciech Zwiefka, David Phillips
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@ -7,7 +7,8 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c multi.c \ memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c multi.c \
content_encoding.c share.c http_digest.c md5.c http_negotiate.c \ content_encoding.c share.c http_digest.c md5.c http_negotiate.c \
http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \ http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \
hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \
select.c
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
@ -16,4 +17,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
http_chunks.h strtok.h connect.h llist.h hash.h content_encoding.h \ http_chunks.h strtok.h connect.h llist.h hash.h content_encoding.h \
share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \ share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \
inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \ inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \
setup.h transfer.h setup.h transfer.h select.h

View File

@ -97,6 +97,7 @@
#include "strerror.h" #include "strerror.h"
#include "connect.h" #include "connect.h"
#include "memory.h" #include "memory.h"
#include "select.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
@ -202,9 +203,6 @@ static
int waitconnect(curl_socket_t sockfd, /* socket */ int waitconnect(curl_socket_t sockfd, /* socket */
long timeout_msec) long timeout_msec)
{ {
fd_set fd;
fd_set errfd;
struct timeval interval;
int rc; int rc;
#ifdef mpeix #ifdef mpeix
/* Call this function once now, and ignore the results. We do this to /* Call this function once now, and ignore the results. We do this to
@ -214,18 +212,7 @@ int waitconnect(curl_socket_t sockfd, /* socket */
#endif #endif
/* now select() until we get connect or timeout */ /* now select() until we get connect or timeout */
FD_ZERO(&fd); rc = Curl_select(CURL_SOCKET_BAD, sockfd, timeout_msec);
FD_SET(sockfd, &fd);
FD_ZERO(&errfd);
FD_SET(sockfd, &errfd);
interval.tv_sec = (int)(timeout_msec/1000);
timeout_msec -= interval.tv_sec*1000;
interval.tv_usec = timeout_msec*1000;
rc = select(sockfd+1, NULL, &fd, &errfd, &interval);
if(-1 == rc) if(-1 == rc)
/* error, no connect here, try next */ /* error, no connect here, try next */
return WAITCONN_SELECT_ERROR; return WAITCONN_SELECT_ERROR;
@ -234,7 +221,7 @@ int waitconnect(curl_socket_t sockfd, /* socket */
/* timeout, no connect today */ /* timeout, no connect today */
return WAITCONN_TIMEOUT; return WAITCONN_TIMEOUT;
if(FD_ISSET(sockfd, &errfd)) if(rc & CSELECT_ERR)
/* error condition caught */ /* error condition caught */
return WAITCONN_FDSET_ERROR; return WAITCONN_FDSET_ERROR;

View File

@ -34,9 +34,6 @@
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
@ -96,6 +93,7 @@
#include "strerror.h" #include "strerror.h"
#include "memory.h" #include "memory.h"
#include "inet_ntop.h" #include "inet_ntop.h"
#include "select.h"
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL) #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
#include "inet_ntoa_r.h" #include "inet_ntoa_r.h"
@ -162,8 +160,7 @@ static void freedirs(struct FTP *ftp)
*/ */
static CURLcode AllowServerConnect(struct connectdata *conn) static CURLcode AllowServerConnect(struct connectdata *conn)
{ {
fd_set rdset; int timeout_ms;
struct timeval dt;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
curl_socket_t sock = conn->sock[SECONDARYSOCKET]; curl_socket_t sock = conn->sock[SECONDARYSOCKET];
struct timeval now = Curl_tvnow(); struct timeval now = Curl_tvnow();
@ -171,10 +168,6 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
long timeout = data->set.connecttimeout?data->set.connecttimeout: long timeout = data->set.connecttimeout?data->set.connecttimeout:
(data->set.timeout?data->set.timeout: 0); (data->set.timeout?data->set.timeout: 0);
FD_ZERO(&rdset);
FD_SET(sock, &rdset);
if(timeout) { if(timeout) {
timeout -= timespent; timeout -= timespent;
if(timeout<=0) { if(timeout<=0) {
@ -184,10 +177,9 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
} }
/* we give the server 60 seconds to connect to us, or a custom timeout */ /* we give the server 60 seconds to connect to us, or a custom timeout */
dt.tv_sec = (int)(timeout?timeout:60); timeout_ms = (timeout?timeout:60) * 1000;
dt.tv_usec = 0;
switch (select(sock+1, &rdset, NULL, NULL, &dt)) { switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) {
case -1: /* error */ case -1: /* error */
/* let's die here */ /* let's die here */
failf(data, "Error while waiting for server connect"); failf(data, "Error while waiting for server connect");
@ -250,9 +242,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
ssize_t gotbytes; ssize_t gotbytes;
char *ptr; char *ptr;
long timeout; /* timeout in seconds */ long timeout; /* timeout in seconds */
struct timeval interval; int interval_ms;
fd_set rkeepfd;
fd_set readfd;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
char *line_start; char *line_start;
int code=0; /* default ftp "error code" to return */ int code=0; /* default ftp "error code" to return */
@ -264,13 +254,6 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
if (ftpcode) if (ftpcode)
*ftpcode = 0; /* 0 for errors */ *ftpcode = 0; /* 0 for errors */
FD_ZERO (&readfd); /* clear it */
FD_SET (sockfd, &readfd); /* read socket */
/* get this in a backup variable to be able to restore it on each lap in the
select() loop */
rkeepfd = readfd;
ptr=buf; ptr=buf;
line_start = buf; line_start = buf;
@ -304,11 +287,9 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
} }
if(!ftp->cache) { if(!ftp->cache) {
readfd = rkeepfd; /* set every lap */ interval_ms = 1 * 1000; /* use 1 second timeout intervals */
interval.tv_sec = 1; /* use 1 second timeout intervals */
interval.tv_usec = 0;
switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) { switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) {
case -1: /* select() error, stop reading */ case -1: /* select() error, stop reading */
result = CURLE_RECV_ERROR; result = CURLE_RECV_ERROR;
failf(data, "FTP response aborted due to select() error: %d", errno); failf(data, "FTP response aborted due to select() error: %d", errno);

View File

@ -74,10 +74,6 @@
#include <sys/param.h> #include <sys/param.h>
#endif #endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif #endif
#include "urldata.h" #include "urldata.h"
@ -98,6 +94,7 @@
#include "hostip.h" #include "hostip.h"
#include "http.h" #include "http.h"
#include "memory.h" #include "memory.h"
#include "select.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -935,9 +932,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
ssize_t gotbytes; ssize_t gotbytes;
char *ptr; char *ptr;
long timeout = 3600; /* default timeout in seconds */ long timeout = 3600; /* default timeout in seconds */
struct timeval interval; int interval_ms;
fd_set rkeepfd;
fd_set readfd;
char *line_start; char *line_start;
char *host_port; char *host_port;
curl_socket_t tunnelsocket = conn->sock[sockindex]; curl_socket_t tunnelsocket = conn->sock[sockindex];
@ -985,13 +980,6 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
if(result) if(result)
return result; return result;
FD_ZERO (&readfd); /* clear it */
FD_SET (tunnelsocket, &readfd); /* read socket */
/* get this in a backup variable to be able to restore it on each lap in
the select() loop */
rkeepfd = readfd;
ptr=data->state.buffer; ptr=data->state.buffer;
line_start = ptr; line_start = ptr;
@ -1000,9 +988,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
keepon=TRUE; keepon=TRUE;
while((nread<BUFSIZE) && (keepon && !error)) { while((nread<BUFSIZE) && (keepon && !error)) {
readfd = rkeepfd; /* set every lap */ interval_ms = 1; /* timeout each second and check the timeout */
interval.tv_sec = 1; /* timeout each second and check the timeout */
interval.tv_usec = 0;
if(data->set.timeout) { if(data->set.timeout) {
/* if timeout is requested, find out how much remaining time we have */ /* if timeout is requested, find out how much remaining time we have */
@ -1015,7 +1001,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
} }
} }
switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) { switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, interval_ms)) {
case -1: /* select() error, stop reading */ case -1: /* select() error, stop reading */
error = SELECT_ERROR; error = SELECT_ERROR;
failf(data, "Proxy CONNECT aborted due to select() error"); failf(data, "Proxy CONNECT aborted due to select() error");

231
lib/select.c Normal file
View File

@ -0,0 +1,231 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#include "select.h"
/*
* This is an internal function used for waiting for read or write
* events on single file descriptors. It attempts to replace select()
* in order to avoid limits with FD_SETSIZE.
*
* Return values:
* -1 = system call error
* 0 = timeout
* CSELECT_IN | CSELECT_OUT | CSELECT_ERR
*/
int Curl_select(int readfd, int writefd, int timeout_ms)
{
#ifdef HAVE_POLL_FINE
struct pollfd pfd[2];
int num;
int r;
int ret;
num = 0;
if (readfd != CURL_SOCKET_BAD) {
pfd[num].fd = readfd;
pfd[num].events = POLLIN;
num++;
}
if (writefd != CURL_SOCKET_BAD) {
pfd[num].fd = writefd;
pfd[num].events = POLLOUT;
num++;
}
r = poll(pfd, num, timeout_ms);
if (r < 0)
return -1;
if (r == 0)
return 0;
ret = 0;
num = 0;
if (readfd != CURL_SOCKET_BAD) {
if (pfd[num].revents & POLLIN)
ret |= CSELECT_IN;
if (pfd[num].revents & POLLERR)
ret |= CSELECT_ERR;
num++;
}
if (writefd != CURL_SOCKET_BAD) {
if (pfd[num].revents & POLLOUT)
ret |= CSELECT_OUT;
if (pfd[num].revents & POLLERR)
ret |= CSELECT_ERR;
}
return ret;
#else
struct timeval timeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
int maxfd;
int r;
int ret;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
FD_ZERO(&fds_err);
maxfd = -1;
FD_ZERO(&fds_read);
if (readfd != CURL_SOCKET_BAD) {
if ((readfd < 0) || (readfd >= FD_SETSIZE)) {
errno = EINVAL;
return -1;
}
FD_SET(readfd, &fds_read);
FD_SET(readfd, &fds_err);
maxfd = readfd;
}
FD_ZERO(&fds_write);
if (writefd != CURL_SOCKET_BAD) {
if ((writefd < 0) || (writefd >= FD_SETSIZE)) {
errno = EINVAL;
return -1;
}
FD_SET(writefd, &fds_write);
FD_SET(writefd, &fds_err);
if (writefd > maxfd)
maxfd = writefd;
}
r = select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout);
if (r < 0)
return -1;
if (r == 0)
return 0;
ret = 0;
if (readfd != CURL_SOCKET_BAD) {
if (FD_ISSET(readfd, &fds_read))
ret |= CSELECT_IN;
if (FD_ISSET(readfd, &fds_err))
ret |= CSELECT_ERR;
}
if (writefd != CURL_SOCKET_BAD) {
if (FD_ISSET(writefd, &fds_write))
ret |= CSELECT_OUT;
if (FD_ISSET(writefd, &fds_err))
ret |= CSELECT_ERR;
}
return ret;
#endif
}
/*
* This is a wrapper around poll(). If poll() does not exist, then
* select() is used instead. An error is returned if select() is
* being used and a file descriptor too large for FD_SETSIZE.
*
* Return values:
* -1 = system call error or fd >= FD_SETSIZE
* 0 = timeout
* 1 = number of structures with non zero revent fields
*/
int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
{
#ifdef HAVE_POLL_FINE
return poll(ufds, nfds, timeout_ms);
#else
struct timeval timeout;
struct timeval *ptimeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
int maxfd;
int r;
unsigned int i;
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
FD_ZERO(&fds_err);
maxfd = -1;
for (i = 0; i < nfds; i++) {
if (ufds[i].fd < 0)
continue;
if (ufds[i].fd >= FD_SETSIZE) {
errno = EINVAL;
return -1;
}
if (ufds[i].fd > maxfd)
maxfd = ufds[i].fd;
if (ufds[i].events & POLLIN)
FD_SET(ufds[i].fd, &fds_read);
if (ufds[i].events & POLLOUT)
FD_SET(ufds[i].fd, &fds_write);
if (ufds[i].events & POLLERR)
FD_SET(ufds[i].fd, &fds_err);
}
if (timeout_ms < 0) {
ptimeout = NULL; /* wait forever */
} else {
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
ptimeout = &timeout;
}
r = select(maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
if (r < 0)
return -1;
if (r == 0)
return 0;
r = 0;
for (i = 0; i < nfds; i++) {
ufds[i].revents = 0;
if (ufds[i].fd < 0)
continue;
if (FD_ISSET(ufds[i].fd, &fds_read))
ufds[i].revents |= POLLIN;
if (FD_ISSET(ufds[i].fd, &fds_write))
ufds[i].revents |= POLLOUT;
if (FD_ISSET(ufds[i].fd, &fds_err))
ufds[i].revents |= POLLERR;
if (ufds[i].revents != 0)
r++;
}
return r;
#endif
}

57
lib/select.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef __SELECT_H
#define __SELECT_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#else
#define POLLIN 0x01
#define POLLPRI 0x02
#define POLLOUT 0x04
#define POLLERR 0x08
#define POLLHUP 0x10
#define POLLNVAL 0x20
struct pollfd
{
int fd;
short events;
short revents;
};
#endif
#define CSELECT_IN 0x01
#define CSELECT_OUT 0x02
#define CSELECT_ERR 0x04
int Curl_select(int readfd, int writefd, int timeout_ms);
int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms);
#endif

View File

@ -46,6 +46,7 @@
#include "ssluse.h" #include "ssluse.h"
#include "connect.h" /* Curl_ourerrno() proto */ #include "connect.h" /* Curl_ourerrno() proto */
#include "strequal.h" #include "strequal.h"
#include "select.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */ #define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -1260,9 +1261,8 @@ Curl_SSLConnect(struct connectdata *conn,
SSL_set_fd(connssl->handle, sockfd); SSL_set_fd(connssl->handle, sockfd);
while(1) { while(1) {
fd_set writefd; int writefd;
fd_set readfd; int readfd;
struct timeval interval;
long timeout_ms; long timeout_ms;
/* Find out if any timeout is set. If not, use 300 seconds. /* Find out if any timeout is set. If not, use 300 seconds.
@ -1296,8 +1296,8 @@ Curl_SSLConnect(struct connectdata *conn,
timeout_ms= DEFAULT_CONNECT_TIMEOUT; timeout_ms= DEFAULT_CONNECT_TIMEOUT;
FD_ZERO(&writefd); readfd = CURL_SOCKET_BAD;
FD_ZERO(&readfd); writefd = CURL_SOCKET_BAD;
err = SSL_connect(connssl->handle); err = SSL_connect(connssl->handle);
@ -1308,9 +1308,9 @@ Curl_SSLConnect(struct connectdata *conn,
int detail = SSL_get_error(connssl->handle, err); int detail = SSL_get_error(connssl->handle, err);
if(SSL_ERROR_WANT_READ == detail) if(SSL_ERROR_WANT_READ == detail)
FD_SET(sockfd, &readfd); readfd = sockfd;
else if(SSL_ERROR_WANT_WRITE == detail) else if(SSL_ERROR_WANT_WRITE == detail)
FD_SET(sockfd, &writefd); writefd = sockfd;
else { else {
/* untreated error */ /* untreated error */
unsigned long errdetail; unsigned long errdetail;
@ -1373,13 +1373,8 @@ Curl_SSLConnect(struct connectdata *conn,
/* we have been connected fine, get out of the connect loop */ /* we have been connected fine, get out of the connect loop */
break; break;
interval.tv_sec = (int)(timeout_ms/1000);
timeout_ms -= interval.tv_sec*1000;
interval.tv_usec = timeout_ms*1000;
while(1) { while(1) {
what = select(sockfd+1, &readfd, &writefd, NULL, &interval); what = Curl_select(readfd, writefd, timeout_ms);
if(what > 0) if(what > 0)
/* reabable or writable, go loop in the outer loop */ /* reabable or writable, go loop in the outer loop */
break; break;

View File

@ -64,10 +64,6 @@
#include <sys/param.h> #include <sys/param.h>
#endif #endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif #endif
@ -85,6 +81,7 @@
#include "arpa_telnet.h" #include "arpa_telnet.h"
#include "memory.h" #include "memory.h"
#include "select.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
@ -1088,8 +1085,8 @@ CURLcode Curl_telnet(struct connectdata *conn)
DWORD waitret; DWORD waitret;
DWORD readfile_read; DWORD readfile_read;
#else #else
fd_set readfd; int interval_ms;
fd_set keepfd; struct pollfd pfd[2];
#endif #endif
ssize_t nread; ssize_t nread;
bool keepon = TRUE; bool keepon = TRUE;
@ -1308,27 +1305,21 @@ CURLcode Curl_telnet(struct connectdata *conn)
if (!FreeLibrary(wsock2)) if (!FreeLibrary(wsock2))
infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError()); infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError());
#else #else
FD_ZERO (&readfd); /* clear it */ pfd[0].fd = sockfd;
FD_SET (sockfd, &readfd); pfd[0].events = POLLIN;
FD_SET (0, &readfd); pfd[1].fd = 0;
pfd[1].events = POLLIN;
keepfd = readfd; interval_ms = 1 * 1000;
while (keepon) { while (keepon) {
struct timeval interval; switch (Curl_poll(pfd, 2, interval_ms)) {
readfd = keepfd; /* set this every lap in the loop */
interval.tv_sec = 1;
interval.tv_usec = 0;
switch (select (sockfd + 1, &readfd, NULL, NULL, &interval)) {
case -1: /* error, stop reading */ case -1: /* error, stop reading */
keepon = FALSE; keepon = FALSE;
continue; continue;
case 0: /* timeout */ case 0: /* timeout */
break; break;
default: /* read! */ default: /* read! */
if(FD_ISSET(0, &readfd)) { /* read from stdin */ if(pfd[1].revents & POLLIN) { /* read from stdin */
unsigned char outbuf[2]; unsigned char outbuf[2];
int out_count = 0; int out_count = 0;
ssize_t bytes_written; ssize_t bytes_written;
@ -1347,7 +1338,7 @@ CURLcode Curl_telnet(struct connectdata *conn)
} }
} }
if(FD_ISSET(sockfd, &readfd)) { if(pfd[0].revents & POLLIN) {
/* This OUGHT to check the return code... */ /* This OUGHT to check the return code... */
(void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);

View File

@ -75,9 +75,6 @@
#include <sys/select.h> #include <sys/select.h>
#endif #endif
#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#ifndef HAVE_SOCKET #ifndef HAVE_SOCKET
#error "We can't compile without socket() support!" #error "We can't compile without socket() support!"
#endif #endif
@ -103,6 +100,7 @@
#include "http_negotiate.h" #include "http_negotiate.h"
#include "share.h" #include "share.h"
#include "memory.h" #include "memory.h"
#include "select.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -118,10 +116,6 @@ enum {
KEEP_WRITE KEEP_WRITE
}; };
/* We keep this static and global since this is read-only and NEVER
changed. It should just remain a blanked-out timeout value. */
static struct timeval notimeout={0,0};
/* /*
* This function will call the read callback to fill our buffer with data * This function will call the read callback to fill our buffer with data
* to upload. * to upload.
@ -213,43 +207,28 @@ CURLcode Curl_readwrite(struct connectdata *conn,
ssize_t nread; /* number of bytes read */ ssize_t nread; /* number of bytes read */
int didwhat=0; int didwhat=0;
/* These two are used only if no other select() or _fdset() have been int fd_read;
invoked before this. This typicly happens if you use the multi interface int fd_write;
and call curl_multi_perform() without calling curl_multi_fdset() int select_res;
first. */
fd_set extrareadfd;
fd_set extrawritefd;
fd_set *readfdp = k->readfdp;
fd_set *writefdp = k->writefdp;
curl_off_t contentlength; curl_off_t contentlength;
if((k->keepon & KEEP_READ) && !readfdp) { if(k->keepon & KEEP_READ)
/* reading is requested, but no socket descriptor pointer was set */ fd_read = conn->sockfd;
FD_ZERO(&extrareadfd); else
FD_SET(conn->sockfd, &extrareadfd); fd_read = CURL_SOCKET_BAD;
readfdp = &extrareadfd;
/* no write, no exceptions, no timeout */ if(k->keepon & KEEP_WRITE)
select(conn->sockfd+1, readfdp, NULL, NULL, &notimeout); fd_write = conn->writesockfd;
} else
if((k->keepon & KEEP_WRITE) && !writefdp) { fd_write = CURL_SOCKET_BAD;
/* writing is requested, but no socket descriptor pointer was set */
FD_ZERO(&extrawritefd);
FD_SET(conn->writesockfd, &extrawritefd);
writefdp = &extrawritefd;
/* no read, no exceptions, no timeout */ select_res = Curl_select(fd_read, fd_write, 0);
select(conn->writesockfd+1, NULL, writefdp, NULL, &notimeout);
}
do { do {
/* If we still have reading to do, we check if we have a readable /* If we still have reading to do, we check if we have a readable
socket. Sometimes the reafdp is NULL, if no fd_set was done using socket. */
the multi interface and then we can do nothing but to attempt a if((k->keepon & KEEP_READ) && (select_res & CSELECT_IN)) {
read to be sure. */
if((k->keepon & KEEP_READ) &&
(!readfdp || FD_ISSET(conn->sockfd, readfdp))) {
bool is_empty_data = FALSE; bool is_empty_data = FALSE;
@ -291,7 +270,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
we bail out from this! */ we bail out from this! */
else if (0 >= nread) { else if (0 >= nread) {
k->keepon &= ~KEEP_READ; k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
break; break;
} }
@ -436,7 +414,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if (k->write_after_100_header) { if (k->write_after_100_header) {
k->write_after_100_header = FALSE; k->write_after_100_header = FALSE;
FD_SET (conn->writesockfd, &k->writefd); /* write */
k->keepon |= KEEP_WRITE; k->keepon |= KEEP_WRITE;
k->wkeepfd = k->writefd; k->wkeepfd = k->writefd;
} }
@ -453,7 +430,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
*/ */
k->write_after_100_header = FALSE; k->write_after_100_header = FALSE;
k->keepon &= ~KEEP_WRITE; k->keepon &= ~KEEP_WRITE;
FD_ZERO(&k->wkeepfd);
} }
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
@ -550,7 +526,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(stop_reading) { if(stop_reading) {
/* we make sure that this socket isn't read more now */ /* we make sure that this socket isn't read more now */
k->keepon &= ~KEEP_READ; k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
} }
break; /* exit header line loop */ break; /* exit header line loop */
@ -951,7 +926,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
/* Abort after the headers if "follow Location" is set /* Abort after the headers if "follow Location" is set
and we're set to close anyway. */ and we're set to close anyway. */
k->keepon &= ~KEEP_READ; k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
*done = TRUE; *done = TRUE;
return CURLE_OK; return CURLE_OK;
} }
@ -1040,7 +1014,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
else if(CHUNKE_STOP == res) { else if(CHUNKE_STOP == res) {
/* we're done reading chunks! */ /* we're done reading chunks! */
k->keepon &= ~KEEP_READ; /* read no more */ k->keepon &= ~KEEP_READ; /* read no more */
FD_ZERO(&k->rkeepfd);
/* There are now possibly N number of bytes at the end of the /* There are now possibly N number of bytes at the end of the
str buffer that weren't written to the client, but we don't str buffer that weren't written to the client, but we don't
@ -1057,7 +1030,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
nread = 0; nread = 0;
k->keepon &= ~KEEP_READ; /* we're done reading */ k->keepon &= ~KEEP_READ; /* we're done reading */
FD_ZERO(&k->rkeepfd);
} }
k->bytecount += nread; k->bytecount += nread;
@ -1125,7 +1097,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
/* if we received nothing, the server closed the connection and we /* if we received nothing, the server closed the connection and we
are done */ are done */
k->keepon &= ~KEEP_READ; k->keepon &= ~KEEP_READ;
FD_ZERO(&k->rkeepfd);
} }
} while(0); } while(0);
@ -1133,11 +1104,8 @@ CURLcode Curl_readwrite(struct connectdata *conn,
} /* if( read from socket ) */ } /* if( read from socket ) */
/* If we still have writing to do, we check if we have a writable /* If we still have writing to do, we check if we have a writable
socket. Sometimes the writefdp is NULL, if no fd_set was done using socket. */
the multi interface and then we can do nothing but to attempt a if((k->keepon & KEEP_WRITE) && (select_res & CSELECT_OUT)) {
write to be sure. */
if((k->keepon & KEEP_WRITE) &&
(!writefdp || FD_ISSET(conn->writesockfd, writefdp)) ) {
/* write */ /* write */
int i, si; int i, si;
@ -1173,7 +1141,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
go into the Expect: 100 state and await such a header */ go into the Expect: 100 state and await such a header */
k->wait100_after_headers = FALSE; /* headers sent */ k->wait100_after_headers = FALSE; /* headers sent */
k->write_after_100_header = TRUE; /* wait for the header */ k->write_after_100_header = TRUE; /* wait for the header */
FD_ZERO (&k->writefd); /* clear it */
k->wkeepfd = k->writefd; /* set the keeper variable */ k->wkeepfd = k->writefd; /* set the keeper variable */
k->keepon &= ~KEEP_WRITE; /* disable writing */ k->keepon &= ~KEEP_WRITE; /* disable writing */
k->start100 = Curl_tvnow(); /* timeout count starts now */ k->start100 = Curl_tvnow(); /* timeout count starts now */
@ -1195,7 +1162,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if (nread<=0) { if (nread<=0) {
/* done */ /* done */
k->keepon &= ~KEEP_WRITE; /* we're done writing */ k->keepon &= ~KEEP_WRITE; /* we're done writing */
FD_ZERO(&k->wkeepfd);
writedone = TRUE; writedone = TRUE;
break; break;
} }
@ -1271,7 +1237,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(k->upload_done) { if(k->upload_done) {
/* switch off writing, we're done! */ /* switch off writing, we're done! */
k->keepon &= ~KEEP_WRITE; /* we're done writing */ k->keepon &= ~KEEP_WRITE; /* we're done writing */
FD_ZERO(&k->wkeepfd);
writedone = TRUE; writedone = TRUE;
} }
} }
@ -1313,7 +1278,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(ms > CURL_TIMEOUT_EXPECT_100) { if(ms > CURL_TIMEOUT_EXPECT_100) {
/* we've waited long enough, continue anyway */ /* we've waited long enough, continue anyway */
k->write_after_100_header = FALSE; k->write_after_100_header = FALSE;
FD_SET (conn->writesockfd, &k->writefd); /* write socket */
k->keepon |= KEEP_WRITE; k->keepon |= KEEP_WRITE;
k->wkeepfd = k->writefd; k->wkeepfd = k->writefd;
} }
@ -1405,13 +1369,10 @@ CURLcode Curl_readwrite_init(struct connectdata *conn)
/* we want header and/or body, if neither then don't do this! */ /* we want header and/or body, if neither then don't do this! */
if(conn->bits.getheader || !conn->bits.no_body) { if(conn->bits.getheader || !conn->bits.no_body) {
FD_ZERO (&k->readfd); /* clear it */ if(conn->sockfd != CURL_SOCKET_BAD) {
if(conn->sockfd != CURL_SOCKET_BAD) {
FD_SET (conn->sockfd, &k->readfd); /* read socket */
k->keepon |= KEEP_READ; k->keepon |= KEEP_READ;
} }
FD_ZERO (&k->writefd); /* clear it */
if(conn->writesockfd != CURL_SOCKET_BAD) { if(conn->writesockfd != CURL_SOCKET_BAD) {
/* HTTP 1.1 magic: /* HTTP 1.1 magic:
@ -1433,7 +1394,6 @@ CURLcode Curl_readwrite_init(struct connectdata *conn)
/* when we've sent off the rest of the headers, we must await a /* when we've sent off the rest of the headers, we must await a
100-continue */ 100-continue */
k->wait100_after_headers = TRUE; k->wait100_after_headers = TRUE;
FD_SET (conn->writesockfd, &k->writefd); /* write socket */
k->keepon |= KEEP_WRITE; k->keepon |= KEEP_WRITE;
} }
} }
@ -1521,13 +1481,23 @@ Transfer(struct connectdata *conn)
k->readfdp = &k->readfd; /* store the address of the set */ k->readfdp = &k->readfd; /* store the address of the set */
while (!done) { while (!done) {
struct timeval interval; int fd_read;
k->readfd = k->rkeepfd; /* set these every lap in the loop */ int fd_write;
k->writefd = k->wkeepfd; int interval_ms;
interval.tv_sec = 1;
interval.tv_usec = 0;
switch (select (k->maxfd, k->readfdp, k->writefdp, NULL, &interval)) { interval_ms = 1 * 1000;
if(k->keepon & KEEP_READ)
fd_read = conn->sockfd;
else
fd_read = CURL_SOCKET_BAD;
if(k->keepon & KEEP_WRITE)
fd_write = conn->writesockfd;
else
fd_write = CURL_SOCKET_BAD;
switch (Curl_select(fd_read, fd_write, interval_ms)) {
case -1: /* select() error, stop reading */ case -1: /* select() error, stop reading */
#ifdef EINTR #ifdef EINTR
/* The EINTR is not serious, and it seems you might get this more /* The EINTR is not serious, and it seems you might get this more

View File

@ -64,10 +64,6 @@
#include <sys/param.h> #include <sys/param.h>
#endif #endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef VMS #ifdef VMS
#include <in.h> #include <in.h>
#include <inet.h> #include <inet.h>
@ -77,9 +73,6 @@
#include <setjmp.h> #include <setjmp.h>
#endif #endif
#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#ifndef HAVE_SOCKET #ifndef HAVE_SOCKET
#error "We can't compile without socket() support!" #error "We can't compile without socket() support!"
#endif #endif
@ -127,6 +120,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#include "content_encoding.h" #include "content_encoding.h"
#include "http_digest.h" #include "http_digest.h"
#include "http_negotiate.h" #include "http_negotiate.h"
#include "select.h"
/* And now for the protocols */ /* And now for the protocols */
#include "ftp.h" #include "ftp.h"
@ -1552,16 +1546,8 @@ static bool SocketIsDead(curl_socket_t sock)
{ {
int sval; int sval;
bool ret_val = TRUE; bool ret_val = TRUE;
fd_set check_set;
struct timeval to;
FD_ZERO(&check_set); sval = Curl_select(sock, CURL_SOCKET_BAD, 0);
FD_SET(sock, &check_set);
to.tv_sec = 0;
to.tv_usec = 0;
sval = select(sock + 1, &check_set, 0, 0, &to);
if(sval == 0) if(sval == 0)
/* timeout */ /* timeout */
ret_val = FALSE; ret_val = FALSE;

View File

@ -28,7 +28,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test513 test514 test178 test179 test180 test181 test182 test183 \ test513 test514 test178 test179 test180 test181 test182 test183 \
test184 test185 test186 test187 test188 test189 test191 test192 \ test184 test185 test186 test187 test188 test189 test191 test192 \
test193 test194 test195 test196 test197 test198 test515 test516 \ test193 test194 test195 test196 test197 test198 test515 test516 \
test517 test517 test518
# The following tests have been removed from the dist since they no longer # The following tests have been removed from the dist since they no longer
# work. We need to fix the test suite's FTPS server first, then bring them # work. We need to fix the test suite's FTPS server first, then bring them

45
tests/data/test518 Normal file
View File

@ -0,0 +1,45 @@
#
# Server-side
<reply name="1">
<data>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
ETag: "21025-dc7-39462498"
Accept-Ranges: bytes
Content-Length: 6
Connection: close
Content-Type: text/html
Funny-head: yesyes
<foo>
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
# tool is what to use instead of 'curl'
<tool>
lib518
</tool>
<name>
HTTP GET with more than FD_SETSIZE descriptors open
</name>
<command>
http://%HOSTIP:%HTTPPORT/518
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
# CURLE_FAILED_INIT (2)
<errorcode>
2
</errorcode>
</verify>

View File

@ -39,7 +39,8 @@ SUPPORTFILES = first.c test.h
# These are all libcurl test programs # These are all libcurl test programs
noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 \ noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 \
lib508 lib509 lib510 lib511 lib512 lib513 lib514 lib515 lib516 lib517 lib508 lib509 lib510 lib511 lib512 lib513 lib514 lib515 lib516 lib517 \
lib518
lib500_SOURCES = lib500.c $(SUPPORTFILES) lib500_SOURCES = lib500.c $(SUPPORTFILES)
lib500_LDADD = $(LIBDIR)/libcurl.la lib500_LDADD = $(LIBDIR)/libcurl.la
@ -112,3 +113,7 @@ lib516_DEPENDENCIES = $(LIBDIR)/libcurl.la
lib517_SOURCES = lib517.c $(SUPPORTFILES) lib517_SOURCES = lib517.c $(SUPPORTFILES)
lib517_LDADD = $(LIBDIR)/libcurl.la lib517_LDADD = $(LIBDIR)/libcurl.la
lib517_DEPENDENCIES = $(LIBDIR)/libcurl.la lib517_DEPENDENCIES = $(LIBDIR)/libcurl.la
lib518_SOURCES = lib518.c $(SUPPORTFILES)
lib518_LDADD = $(LIBDIR)/libcurl.la
lib518_DEPENDENCIES = $(LIBDIR)/libcurl.la

47
tests/libtest/lib518.c Normal file
View File

@ -0,0 +1,47 @@
#include "test.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <mprintf.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef FD_SETSIZE
#error "this test requires FD_SETSIZE"
#endif
#define NUM_OPEN (FD_SETSIZE + 10)
int test(char *URL)
{
CURLcode res;
CURL *curl;
int fd[NUM_OPEN];
int i;
/* open a lot of file descriptors */
for (i = 0; i < NUM_OPEN; i++) {
fd[i] = open("/dev/null", O_RDONLY);
if (fd[i] == -1) {
fprintf(stderr, "open: attempt #%i: failed to open /dev/null\n", i);
for (i--; i >= 0; i--)
close(fd[i]);
return CURLE_FAILED_INIT;
}
}
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_HEADER, TRUE);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
for (i = 0; i < NUM_OPEN; i++)
close(fd[i]);
return (int)res;
}