First cut to truly async connect:

- after connect(2), if async the connection is in "connecting" state
	- the first time a request is sent, "connecting" conns are polled
		- in case of success, the request is sent
		- in case still connecting, LDAP_X_CONNECTING is returned;
		  clients are expected to retry later
	- the "async" behavior must be explicitly enabled by setting
	  the LDAP_OPT_CONNECT_ASYNC option

"local" connections need work
This commit is contained in:
Pierangelo Masarati 2007-01-07 19:20:46 +00:00
parent f38eee8df1
commit aa3c9bad3e
5 changed files with 196 additions and 13 deletions

View File

@ -122,6 +122,7 @@ LDAP_BEGIN_DECL
#define LDAP_OPT_REFERRAL_URLS 0x5007 /* Referral URLs */
#define LDAP_OPT_SOCKBUF 0x5008 /* sockbuf */
#define LDAP_OPT_DEFBASE 0x5009 /* searchbase */
#define LDAP_OPT_CONNECT_ASYNC 0x5010 /* create connections asynchronously */
/* OpenLDAP TLS options */
#define LDAP_OPT_X_TLS 0x6000
@ -653,6 +654,7 @@ typedef struct ldapcontrol {
#define LDAP_MORE_RESULTS_TO_RETURN (-15) /* Obsolete */
#define LDAP_CLIENT_LOOP (-16)
#define LDAP_REFERRAL_LIMIT_EXCEEDED (-17)
#define LDAP_X_CONNECTING (-18)
/*

View File

@ -117,6 +117,7 @@ LDAP_BEGIN_DECL
#define LDAP_BOOL_REFERRALS 0
#define LDAP_BOOL_RESTART 1
#define LDAP_BOOL_TLS 3
#define LDAP_BOOL_CONNECT_ASYNC 4
#define LDAP_BOOLEANS unsigned long
#define LDAP_BOOL(n) ((LDAP_BOOLEANS)1 << (n))
@ -510,6 +511,8 @@ LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest,
const struct timeval *tm );
LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb,
int proto, const char *host, int port, int async );
LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s,
struct timeval *tvp );
#if defined(LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND) || \
defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)

View File

@ -242,6 +242,10 @@ ldap_get_option(
return LDAP_OPT_SUCCESS;
case LDAP_OPT_CONNECT_ASYNC:
* (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC);
return LDAP_OPT_SUCCESS;
case LDAP_OPT_RESULT_CODE:
if(ld == NULL) {
/* bad param */
@ -392,6 +396,14 @@ ldap_set_option(
LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART);
}
return LDAP_OPT_SUCCESS;
case LDAP_OPT_CONNECT_ASYNC:
if(invalue == LDAP_OPT_OFF) {
LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC);
} else {
LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC);
}
return LDAP_OPT_SUCCESS;
}
/* options which can withstand invalue == NULL */

View File

@ -214,6 +214,137 @@ ldap_pvt_is_socket_ready(LDAP *ld, int s)
#endif /* HAVE_WINSOCK */
/* NOTE: this is identical to analogous code in os-local.c */
int
ldap_int_poll(
LDAP *ld,
ber_socket_t s,
struct timeval *tvp )
{
int timeout = INFTIM;
struct timeval tv = { 0 };
int rc;
if ( tvp != NULL ) {
tv = *tvp;
timeout = TV2MILLISEC( tvp );
}
osip_debug(ld, "ldap_int_poll: fd: %d tm: %ld\n",
s, tvp ? tvp->tv_sec : -1L, 0);
#ifdef HAVE_POLL
{
struct pollfd fd;
fd.fd = s;
fd.events = POLL_WRITE;
do {
fd.revents = 0;
rc = poll( &fd, 1, timeout );
} while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
if ( rc == AC_SOCKET_ERROR ) {
return rc;
}
if ( timeout == 0 && rc == 0 ) {
return -2;
}
if ( fd.revents & POLL_WRITE ) {
if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
return -1;
}
if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) {
return -1;
}
return 0;
}
}
#else
{
fd_set wfds, *z = NULL;
#ifdef HAVE_WINSOCK
fd_set efds;
#endif
#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK )
if ( s >= FD_SETSIZE ) {
rc = AC_SOCKET_ERROR;
tcp_close( s );
ldap_pvt_set_errno( EMFILE );
return rc;
}
#endif
do {
FD_ZERO(&wfds);
FD_SET(s, &wfds );
#ifdef HAVE_WINSOCK
FD_ZERO(&efds);
FD_SET(s, &efds );
#endif
rc = select( ldap_int_tblsize, z, &wfds,
#ifdef HAVE_WINSOCK
&efds,
#else
z,
#endif
tvp ? &tv : NULL );
} while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
if ( rc == AC_SOCKET_ERROR ) {
return rc;
}
if ( timeout == 0 && rc == 0 ) {
return -2;
}
#ifdef HAVE_WINSOCK
/* This means the connection failed */
if ( FD_ISSET(s, &efds) ) {
int so_errno;
int dummy = sizeof(so_errno);
if ( getsockopt( s, SOL_SOCKET, SO_ERROR,
(char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno )
{
/* impossible */
so_errno = WSAGetLastError();
}
ldap_pvt_set_errno( so_errno );
osip_debug(ld, "ldap_int_poll: error on socket %d: "
"errno: %d (%s)\n", s, errno, sock_errstr( errno ));
return -1;
}
#endif
if ( FD_ISSET(s, &wfds) ) {
#ifndef HAVE_WINSOCK
if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
return -1;
}
#endif
if ( ldap_pvt_ndelay_off(ld, s) == -1 ) {
return -1;
}
return 0;
}
}
#endif
osip_debug(ld, "ldap_int_poll: timed out\n",0,0,0);
ldap_pvt_set_errno( ETIMEDOUT );
return -1;
}
static int
ldap_pvt_connect(LDAP *ld, ber_socket_t s,
struct sockaddr *sin, socklen_t addrlen,
@ -240,7 +371,7 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s,
tv = *opt_tv;
}
osip_debug(ld, "ldap_connect_timeout: fd: %d tm: %ld async: %d\n",
osip_debug(ld, "ldap_pvt_connect: fd: %d tm: %ld async: %d\n",
s, opt_tv ? tv.tv_sec : -1L, async);
if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 )
@ -257,10 +388,14 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s,
return ( -1 );
}
#ifdef notyet
if ( async ) return ( -2 );
#endif
if ( async ) {
/* caller will call ldap_int_poll() as appropriate? */
return ( -2 );
}
rc = ldap_int_poll( ld, s, opt_tv );
#if 0
#ifdef HAVE_POLL
{
struct pollfd fd;
@ -349,9 +484,10 @@ ldap_pvt_connect(LDAP *ld, ber_socket_t s,
}
#endif
osip_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0);
ldap_pvt_set_errno( ETIMEDOUT );
return -1;
#endif
osip_debug(ld, "ldap_pvt_connect: %d\n", rc, 0, 0);
return rc;
}
#ifndef HAVE_INET_ATON
@ -482,7 +618,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
rc = ldap_pvt_connect( ld, s,
sai->ai_addr, sai->ai_addrlen, async );
if ( (rc == 0) || (rc == -2) ) {
if ( rc == 0 || rc == -2 ) {
ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
break;
}

View File

@ -209,11 +209,37 @@ ldap_send_server_request(
}
}
/* async connect... */
if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
ber_socket_t sd = AC_SOCKET_ERROR;
struct timeval tv = { 0 };
ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
/* poll ... */
switch ( ldap_int_poll( ld, sd, &tv ) ) {
case 0:
/* go on! */
lc->lconn_status = LDAP_CONNST_CONNECTED;
break;
case -2:
/* caller will have to call again */
ld->ld_errno = LDAP_X_CONNECTING;
/* fallthru */
default:
/* error */
break;
}
}
if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
ber_free( ber, 1 );
if ( ld->ld_errno == LDAP_SUCCESS ) {
ld->ld_errno = LDAP_SERVER_DOWN;
}
ber_free( ber, 1 );
if ( incparent ) {
/* Forget about the bind */
--parentreq->lr_outrefcnt;
@ -312,6 +338,7 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
int connect, LDAPreqinfo *bind )
{
LDAPConn *lc;
int async = 0;
Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
use_ldsb, connect, (bind != NULL) );
@ -341,8 +368,10 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
if ( connect ) {
LDAPURLDesc **srvp, *srv = NULL;
async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
if ( ldap_int_open_connection( ld, lc, *srvp, 0 ) != -1 )
if ( ldap_int_open_connection( ld, lc, *srvp, async) != -1 )
{
srv = *srvp;
@ -366,7 +395,7 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
lc->lconn_server = ldap_url_dup( srv );
}
lc->lconn_status = LDAP_CONNST_CONNECTED;
lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &ld->ld_conn_mutex );
#endif
@ -663,8 +692,9 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
}
Debug( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt,
( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
? "NeedSocket" : ( lc->lconn_status == LDAP_CONNST_CONNECTING )
? "Connecting" : "Connected", 0 );
? "NeedSocket" :
( lc->lconn_status == LDAP_CONNST_CONNECTING )
? "Connecting" : "Connected", 0 );
Debug( LDAP_DEBUG_TRACE, " last used: %s%s\n",
ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
lc->lconn_rebind_inprogress ? " rebind in progress" : "", 0 );