add socket timeout handling

This commit is contained in:
Oswald Buddenhagen 2015-04-06 16:49:33 +02:00
parent 5c4015aee5
commit 1eb88d4fea
6 changed files with 58 additions and 3 deletions

4
NEWS
View File

@ -1,3 +1,7 @@
[1.3.0]
Network timeout handling has been added.
[1.2.0]
The 'isync' compatibility wrapper is now deprecated.

3
TODO
View File

@ -8,8 +8,7 @@ won't cause the same error message for every attached store.
make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
network timeout handling in general would be a good idea.
lock timeout handling, too.
uidvalidity lock timeout handling would be a good idea.
add message expiration based on arrival date (message date would be too
unreliable). MaxAge; probably mutually exclusive to MaxMessages.

View File

@ -319,6 +319,7 @@ send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
*ctx->in_progress_append = cmd;
ctx->in_progress_append = &cmd->next;
ctx->num_in_progress++;
socket_expect_read( &ctx->conn, 1 );
return 0;
bail:
@ -371,6 +372,7 @@ cancel_submitted_imap_cmds( imap_store_t *ctx )
{
struct imap_cmd *cmd;
socket_expect_read( &ctx->conn, 0 );
while ((cmd = ctx->in_progress)) {
ctx->in_progress = cmd->next;
/* don't update num_in_progress and in_progress_append - store is dead */
@ -1316,6 +1318,7 @@ imap_socket_read( void *aux )
error( "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
break; /* this may mean anything, so prefer not to spam the log */
} else if (*arg == '+') {
socket_expect_read( &ctx->conn, 0 );
/* There can be any number of commands in flight, but only the last
* one can require a continuation, as it enforces a round-trip. */
cmdp = (struct imap_cmd *)((char *)ctx->in_progress_append -
@ -1340,6 +1343,7 @@ imap_socket_read( void *aux )
error( "IMAP error: unexpected command continuation request\n" );
break;
}
socket_expect_read( &ctx->conn, 1 );
} else {
tag = atoi( arg );
for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
@ -1350,7 +1354,8 @@ imap_socket_read( void *aux )
gottag:
if (!(*pcmdp = cmdp->next))
ctx->in_progress_append = pcmdp;
ctx->num_in_progress--;
if (!--ctx->num_in_progress)
socket_expect_read( &ctx->conn, 0 );
arg = next_arg( &cmd );
if (!arg) {
error( "IMAP error: malformed tagged response\n" );
@ -1614,6 +1619,8 @@ imap_open_store_connected( int ok, void *aux )
else if (srvc->ssl_type == SSL_IMAPS)
socket_start_tls( &ctx->conn, imap_open_store_tlsstarted1 );
#endif
else
socket_expect_read( &ctx->conn, 1 );
}
#ifdef HAVE_LIBSSL
@ -1624,12 +1631,15 @@ imap_open_store_tlsstarted1( int ok, void *aux )
if (!ok)
imap_open_store_ssl_bail( ctx );
else
socket_expect_read( &ctx->conn, 1 );
}
#endif
static void
imap_open_store_greeted( imap_store_t *ctx )
{
socket_expect_read( &ctx->conn, 0 );
if (!ctx->caps)
imap_exec( ctx, 0, imap_open_store_p2, "CAPABILITY" );
else
@ -2694,6 +2704,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} else
return 0;
server->sconf.timeout = 20;
#ifdef HAVE_LIBSSL
server->ssl_type = -1;
server->sconf.ssl_versions = -1;
@ -2729,6 +2740,8 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
server->pass_cmd = nfstrdup( cfg->val );
else if (!strcasecmp( "Port", cfg->cmd ))
server->sconf.port = parse_int( cfg );
else if (!strcasecmp( "Timeout", cfg->cmd ))
server->sconf.timeout = parse_int( cfg );
else if (!strcasecmp( "PipelineDepth", cfg->cmd )) {
if ((server->max_in_progress = parse_int( cfg )) < 1) {
error( "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line );

View File

@ -277,6 +277,12 @@ Specify the TCP port number of the IMAP server. (Default: 143 for IMAP,
If \fBTunnel\fR is used, this setting is ignored.
..
.TP
\fBTimeout\fR \fItimeout\fR
Specify the connect and data timeout for the IMAP server in seconds.
Zero means unlimited.
(Default: \fI20\fR)
..
.TP
\fBUser\fR \fIusername\fR
Specify the login name on the IMAP server.
..

View File

@ -260,6 +260,7 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext );
SSL_set_fd( conn->ssl, conn->fd );
SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER );
socket_expect_read( conn, 1 );
conn->state = SCK_STARTTLS;
start_tls_p2( conn );
}
@ -279,6 +280,7 @@ start_tls_p2( conn_t *conn )
static void start_tls_p3( conn_t *conn, int ok )
{
socket_expect_read( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.starttls( ok, conn->callback_aux );
}
@ -324,6 +326,7 @@ socket_start_deflate( conn_t *conn )
static void socket_fd_cb( int, void * );
static void socket_fake_cb( void * );
static void socket_timeout_cb( void * );
static void socket_connect_one( conn_t * );
static void socket_connect_failed( conn_t * );
@ -337,6 +340,7 @@ socket_open_internal( conn_t *sock, int fd )
fcntl( fd, F_SETFL, O_NONBLOCK );
init_notifier( &sock->notify, fd, socket_fd_cb, sock );
init_wakeup( &sock->fd_fake, socket_fake_cb, sock );
init_wakeup( &sock->fd_timeout, socket_timeout_cb, sock );
}
static void
@ -344,6 +348,7 @@ socket_close_internal( conn_t *sock )
{
wipe_notifier( &sock->notify );
wipe_wakeup( &sock->fd_fake );
wipe_wakeup( &sock->fd_timeout );
close( sock->fd );
sock->fd = -1;
}
@ -482,6 +487,7 @@ socket_connect_one( conn_t *sock )
return;
}
conf_notifier( &sock->notify, 0, POLLOUT );
socket_expect_read( sock, 1 );
sock->state = SCK_CONNECTING;
info( "\v\n" );
return;
@ -512,6 +518,7 @@ socket_connected( conn_t *conn )
freeaddrinfo( conn->addrs );
#endif
conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_read( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.connect( 1, conn->callback_aux );
}
@ -579,6 +586,8 @@ do_read( conn_t *sock, char *buf, int len )
int n;
assert( sock->fd >= 0 );
if (pending_wakeup( &sock->fd_timeout ))
conf_wakeup( &sock->fd_timeout, sock->conf->timeout );
#ifdef HAVE_LIBSSL
if (sock->ssl) {
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
@ -662,6 +671,13 @@ socket_fill( conn_t *sock )
}
}
void
socket_expect_read( conn_t *conn, int expect )
{
if (conn->conf->timeout > 0 && expect != pending_wakeup( &conn->fd_timeout ))
conf_wakeup( &conn->fd_timeout, expect ? conn->conf->timeout : -1 );
}
int
socket_read( conn_t *conn, char *buf, int len )
{
@ -970,6 +986,20 @@ socket_fake_cb( void *aux )
do_queued_write( conn );
}
static void
socket_timeout_cb( void *aux )
{
conn_t *conn = (conn_t *)aux;
if (conn->state == SCK_CONNECTING) {
errno = ETIMEDOUT;
socket_connect_failed( conn );
} else {
error( "Socket error on %s: timeout.\n", conn->name );
socket_fail( conn );
}
}
#ifdef HAVE_LIBZ
static void
z_fake_cb( void *aux )

View File

@ -47,6 +47,7 @@ typedef struct server_conf {
char *tunnel;
char *host;
int port;
int timeout;
#ifdef HAVE_LIBSSL
char *cert_file;
char system_certs;
@ -96,6 +97,7 @@ typedef struct {
notifier_t notify;
wakeup_t fd_fake;
wakeup_t fd_timeout;
/* writing */
buff_chunk_t *append_buf; /* accumulating buffer */
@ -137,6 +139,7 @@ void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) );
void socket_start_tls(conn_t *conn, void (*cb)( int ok, void *aux ) );
void socket_start_deflate( conn_t *conn );
void socket_close( conn_t *sock );
void socket_expect_read( conn_t *sock, int expect );
int socket_read( conn_t *sock, char *buf, int len ); /* never waits */
char *socket_read_line( conn_t *sock ); /* don't free return value; never waits */
typedef enum { KeepOwn = 0, GiveOwn } ownership_t;