mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
introduced non-blocking connects
This commit is contained in:
parent
6ca45beaed
commit
c5fdeef41d
@ -59,7 +59,7 @@ escape.c mprintf.c telnet.c \
|
||||
escape.h getpass.c netrc.c telnet.h \
|
||||
getinfo.c getinfo.h transfer.c strequal.c strequal.h easy.c \
|
||||
security.h security.c krb4.c krb4.h memdebug.c memdebug.h inet_ntoa_r.h \
|
||||
http_chunks.c http_chunks.h strtok.c strtok.h
|
||||
http_chunks.c http_chunks.h strtok.c strtok.h connect.c connect.h
|
||||
|
||||
noinst_HEADERS = setup.h transfer.h
|
||||
|
||||
|
275
lib/connect.c
Normal file
275
lib/connect.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*****************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* In order to be useful for every potential user, curl and libcurl are
|
||||
* dual-licensed under the MPL and the MIT/X-derivate licenses.
|
||||
*
|
||||
* 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 MPL or the MIT/X-derivate
|
||||
* licenses. You may pick one of these licenses.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* $Id$
|
||||
*****************************************************************************/
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define HAVE_IOCTLSOCKET
|
||||
#include <windows.h>
|
||||
#include <winsock.h>
|
||||
#define EINPROGRESS WSAEINPROGRESS
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
#include "sendf.h"
|
||||
|
||||
/* The last #include file should be: */
|
||||
#ifdef MALLOCDEBUG
|
||||
#include "memdebug.h"
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* Curl_nonblock
|
||||
*
|
||||
* Description:
|
||||
* Set the socket to either blocking or non-blocking mode.
|
||||
*/
|
||||
static
|
||||
int nonblock(int socket, /* operate on this */
|
||||
int nonblock /* TRUE or FALSE */)
|
||||
{
|
||||
#undef SETBLOCK
|
||||
#ifdef HAVE_O_NONBLOCK
|
||||
int flags;
|
||||
|
||||
flags = fcntl(socket, F_GETFL, 0);
|
||||
if (TRUE == nonblock)
|
||||
return fcntl(socket, F_SETFL, flags | O_NONBLOCK);
|
||||
else
|
||||
return fcntl(socket, F_SETFL, flags & (~O_NONBLOCK));
|
||||
#define SETBLOCK 1
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FIONBIO
|
||||
int flags;
|
||||
|
||||
flags = nonblock;
|
||||
return ioctl(socket, FIONBIO, &flags);
|
||||
#define SETBLOCK 2
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IOCTLSOCKET
|
||||
int flags;
|
||||
flags = nonblock;
|
||||
return ioctlsocket(socket, FIONBIO, &flags);
|
||||
#define SETBLOCK 3
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IOCTLSOCKET_CASE
|
||||
return IoctlSocket(socket, FIONBIO, (long)nonblock);
|
||||
#define SETBLOCK 4
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DISABLED_NONBLOCKING
|
||||
return 0; /* returns success */
|
||||
#define SETBLOCK 5
|
||||
#endif
|
||||
|
||||
#ifndef SETBLOCK
|
||||
#error "no non-blocking method was found/used/set"
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 0 on fine connect, -1 on error and 1 on timeout.
|
||||
*/
|
||||
static
|
||||
int waitconnect(int sockfd, /* socket */
|
||||
int timeout_msec)
|
||||
{
|
||||
fd_set fd;
|
||||
struct timeval interval;
|
||||
int rc;
|
||||
|
||||
/* now select() until we get connect or timeout */
|
||||
FD_ZERO(&fd);
|
||||
FD_SET(sockfd, &fd);
|
||||
|
||||
interval.tv_sec = timeout_msec/1000;
|
||||
timeout_msec -= interval.tv_sec*1000;
|
||||
|
||||
interval.tv_usec = timeout_msec*1000;
|
||||
|
||||
rc = select(sockfd+1, NULL, &fd, NULL, &interval);
|
||||
if(-1 == rc)
|
||||
/* error, no connect here, try next */
|
||||
return -1;
|
||||
|
||||
else if(0 == rc)
|
||||
/* timeout, no connect today */
|
||||
return 1;
|
||||
|
||||
/* we have a connect! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TCP connect to the given host with timeout, proxy or remote doesn't matter.
|
||||
* There might be more than one IP address to try out. Fill in the passed
|
||||
* pointer with the connected socket.
|
||||
*/
|
||||
|
||||
CURLcode Curl_connecthost(struct connectdata *conn,
|
||||
int sockfd, /* input socket, or -1 if none */
|
||||
int *socket)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
int rc;
|
||||
|
||||
#ifdef ENABLE_IPV6
|
||||
/*
|
||||
* Connecting with IPv6 support is so much easier and cleanly done
|
||||
*/
|
||||
if(sockfd != -1)
|
||||
/* don't use any previous one, it might be of wrong type */
|
||||
sclose(sockfd);
|
||||
sockfd = -1; /* none! */
|
||||
for (ai = conn->hp; ai; ai = ai->ai_next) {
|
||||
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (sockfd < 0)
|
||||
continue;
|
||||
|
||||
/* set socket non-blocking */
|
||||
nonblock(sockfd, TRUE);
|
||||
|
||||
rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
|
||||
|
||||
if(0 == rc)
|
||||
/* direct connect, awesome! */
|
||||
break;
|
||||
|
||||
/* asynchronous connect, wait for connect or timeout */
|
||||
rc = waitconnect(sockfd, timeout);
|
||||
if(0 != rc) {
|
||||
/* connect failed or timed out */
|
||||
sclose(sockfd);
|
||||
sockfd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* now disable the non-blocking mode again */
|
||||
nonblock(sockfd, FALSE);
|
||||
break;
|
||||
}
|
||||
conn->ai = ai;
|
||||
if (sockfd < 0) {
|
||||
failf(data, strerror(errno));
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Connecting with IPv4-only support
|
||||
*/
|
||||
int aliasindex;
|
||||
int timeout_ms = 10000; /* while testing */
|
||||
|
||||
/* non-block socket */
|
||||
nonblock(sockfd, TRUE);
|
||||
|
||||
/* This is the loop that attempts to connect to all IP-addresses we
|
||||
know for the given host. One by one. */
|
||||
for(rc=-1, aliasindex=0;
|
||||
rc && (struct in_addr *)conn->hp->h_addr_list[aliasindex];
|
||||
aliasindex++) {
|
||||
|
||||
/* copy this particular name info to the conn struct as it might
|
||||
be used later in the krb4 "system" */
|
||||
memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
|
||||
memcpy((char *)&(conn->serv_addr.sin_addr),
|
||||
(struct in_addr *)conn->hp->h_addr_list[aliasindex],
|
||||
sizeof(struct in_addr));
|
||||
conn->serv_addr.sin_family = conn->hp->h_addrtype;
|
||||
conn->serv_addr.sin_port = htons(conn->port);
|
||||
|
||||
rc = connect(sockfd, (struct sockaddr *)&(conn->serv_addr),
|
||||
sizeof(conn->serv_addr));
|
||||
|
||||
if(-1 == rc) {
|
||||
int error;
|
||||
#ifdef WIN32
|
||||
error = (int)GetLastError();
|
||||
#else
|
||||
error = errno;
|
||||
#endif
|
||||
switch (error) {
|
||||
case EINPROGRESS:
|
||||
case EWOULDBLOCK:
|
||||
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
|
||||
/* On some platforms EAGAIN and EWOULDBLOCK are the
|
||||
* same value, and on others they are different, hence
|
||||
* the odd #if
|
||||
*/
|
||||
case EAGAIN:
|
||||
#endif
|
||||
|
||||
/* asynchronous connect, wait for connect or timeout */
|
||||
rc = waitconnect(sockfd, timeout_ms);
|
||||
break;
|
||||
default:
|
||||
/* unknown error, fallthrough and try another address! */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(0 != rc)
|
||||
continue; /* try next address */
|
||||
else
|
||||
break;
|
||||
}
|
||||
if(-1 == rc) {
|
||||
/* no good connect was made */
|
||||
sclose(sockfd);
|
||||
*socket = -1;
|
||||
failf(data, "Couldn't connect to (any) IP address");
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
|
||||
/* now disable the non-blocking mode again */
|
||||
nonblock(sockfd, FALSE);
|
||||
|
||||
#endif
|
||||
|
||||
*socket = sockfd; /* pass this to our parent */
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
29
lib/connect.h
Normal file
29
lib/connect.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __NONBLOCK_H
|
||||
#define __NONBLOCK_H
|
||||
/*****************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* In order to be useful for every potential user, curl and libcurl are
|
||||
* dual-licensed under the MPL and the MIT/X-derivate licenses.
|
||||
*
|
||||
* 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 MPL or the MIT/X-derivate
|
||||
* licenses. You may pick one of these licenses.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* $Id$
|
||||
*****************************************************************************/
|
||||
|
||||
CURLcode Curl_connecthost(struct connectdata *conn,
|
||||
int sockfd, /* input socket, or -1 if none */
|
||||
int *socket);
|
||||
#endif
|
113
lib/url.c
113
lib/url.c
@ -107,6 +107,7 @@
|
||||
#include "file.h"
|
||||
#include "ldap.h"
|
||||
#include "url.h"
|
||||
#include "connect.h"
|
||||
|
||||
#include <curl/types.h>
|
||||
|
||||
@ -1123,12 +1124,6 @@ static CURLcode ConnectPlease(struct SessionHandle *data,
|
||||
|
||||
#ifndef ENABLE_IPV6
|
||||
conn->firstsocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
|
||||
memcpy((char *)&(conn->serv_addr.sin_addr),
|
||||
conn->hp->h_addr, conn->hp->h_length);
|
||||
conn->serv_addr.sin_family = conn->hp->h_addrtype;
|
||||
conn->serv_addr.sin_port = htons(conn->port);
|
||||
#else
|
||||
/* IPv6-style */
|
||||
struct addrinfo *ai;
|
||||
@ -1259,108 +1254,14 @@ static CURLcode ConnectPlease(struct SessionHandle *data,
|
||||
/*************************************************************
|
||||
* Connect to server/proxy
|
||||
*************************************************************/
|
||||
#ifdef ENABLE_IPV6
|
||||
conn->firstsocket = -1;
|
||||
for (ai = conn->hp; ai; ai = ai->ai_next) {
|
||||
conn->firstsocket = socket(ai->ai_family,
|
||||
ai->ai_socktype,
|
||||
ai->ai_protocol);
|
||||
if (conn->firstsocket < 0)
|
||||
continue;
|
||||
|
||||
if (connect(conn->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) {
|
||||
sclose(conn->firstsocket);
|
||||
conn->firstsocket = -1;
|
||||
continue;
|
||||
return Curl_connecthost(conn,
|
||||
conn->firstsocket, /* might be bind()ed */
|
||||
&conn->firstsocket);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
conn->ai = ai;
|
||||
if (conn->firstsocket < 0) {
|
||||
failf(data, strerror(errno));
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
#else
|
||||
/* non-zero nonblock value sets socket as nonblocking under Win32 */
|
||||
#if defined(WIN32)
|
||||
FD_ZERO (&connectfd);
|
||||
FD_SET(conn->firstsocket, &connectfd);
|
||||
if (conn->data->set.connecttimeout > 0) {
|
||||
nonblock = 1;
|
||||
}
|
||||
ioctlsocket(conn->firstsocket, FIONBIO, &nonblock);
|
||||
#endif
|
||||
if (connect(conn->firstsocket,
|
||||
(struct sockaddr *) &(conn->serv_addr),
|
||||
sizeof(conn->serv_addr)
|
||||
) < 0) {
|
||||
#if defined(WIN32)
|
||||
conntimeout.tv_sec = conn->data->set.connecttimeout;
|
||||
conntimeout.tv_usec = 0;
|
||||
if(-1 != select (conn->firstsocket + 1, NULL, &connectfd, NULL, &conntimeout)) {
|
||||
if (FD_ISSET(conn->firstsocket, &connectfd)) {
|
||||
/* shut off non-blocking again */
|
||||
nonblock = 0;
|
||||
ioctlsocket(conn->firstsocket, FIONBIO, &nonblock);
|
||||
return CURLE_OK;
|
||||
}
|
||||
else
|
||||
errno = EINTR;
|
||||
}
|
||||
#endif
|
||||
switch(errno) {
|
||||
#ifdef ECONNREFUSED
|
||||
/* this should be made nicer */
|
||||
case ECONNREFUSED:
|
||||
failf(data, "Connection refused");
|
||||
break;
|
||||
case EFAULT:
|
||||
failf(data, "Invalid socket address: %d",errno);
|
||||
break;
|
||||
case EISCONN:
|
||||
failf(data, "Socket already connected: %d",errno);
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
failf(data, "Timeout while accepting connection, server busy: %d",errno);
|
||||
break;
|
||||
case ENETUNREACH:
|
||||
failf(data, "Network is unreachable: %d",errno);
|
||||
break;
|
||||
case EADDRINUSE:
|
||||
failf(data, "Local address already in use: %d",errno);
|
||||
break;
|
||||
case EINPROGRESS:
|
||||
failf(data, "Socket is nonblocking and connection can not be completed immediately: %d",errno);
|
||||
break;
|
||||
case EALREADY:
|
||||
failf(data, "Socket is nonblocking and a previous connection attempt not completed: %d",errno);
|
||||
break;
|
||||
case EAGAIN:
|
||||
failf(data, "No more free local ports: %d",errno);
|
||||
break;
|
||||
case EACCES:
|
||||
case EPERM:
|
||||
failf(data, "Attempt to connect to broadcast address without socket broadcast flag or local firewall rule violated: %d",errno);
|
||||
break;
|
||||
#endif
|
||||
case EINTR:
|
||||
failf(data, "Connection timed out");
|
||||
break;
|
||||
default:
|
||||
failf(data, "Can't connect to server: %d", errno);
|
||||
break;
|
||||
}
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode Connect(struct SessionHandle *data,
|
||||
static CURLcode CreateConnection(struct SessionHandle *data,
|
||||
struct connectdata **in_connect,
|
||||
bool allow_port) /* allow data->set.use_port ? */
|
||||
bool allow_port) /* allow set.use_port? */
|
||||
{
|
||||
char *tmp;
|
||||
char *buf;
|
||||
@ -2296,7 +2197,7 @@ CURLcode Curl_connect(struct SessionHandle *data,
|
||||
struct connectdata *conn;
|
||||
|
||||
/* call the stuff that needs to be called */
|
||||
code = Connect(data, in_connect, allow_port);
|
||||
code = CreateConnection(data, in_connect, allow_port);
|
||||
|
||||
if(CURLE_OK != code) {
|
||||
/* We're not allowed to return failure with memory left allocated
|
||||
|
Loading…
Reference in New Issue
Block a user