DONE: consider callback-aborted transfers premature

This bug report properly identified that when doing SMTP and aborting
the transfer with a callback, it must be considered aborted prematurely
by the code to avoid QUIT etc to be attempted as that would cause a
hang.

The new test case 1507 verifies this behavior.

Reported by: Patricia Muscalu
Bug: http://curl.haxx.se/bug/view.cgi?id=1184
This commit is contained in:
Daniel Stenberg 2013-02-08 13:48:56 +01:00
parent 07f97809b8
commit 72688317ad
5 changed files with 231 additions and 2 deletions

View File

@ -5219,6 +5219,13 @@ CURLcode Curl_done(struct connectdata **connp,
conn->dns_entry = NULL; conn->dns_entry = NULL;
} }
if(status == CURLE_ABORTED_BY_CALLBACK)
/* When we're aborted due to a callback return code it basically have to
be counted as premature as there is trouble ahead if we don't. We have
many callbacks and protocols work differently, we could potentially do
this more fine-grained in the future. */
premature = TRUE;
/* this calls the protocol-specific function pointer previously set */ /* this calls the protocol-specific function pointer previously set */
if(conn->handler->done) if(conn->handler->done)
result = conn->handler->done(conn, status, premature); result = conn->handler->done(conn, status, premature);

View File

@ -93,7 +93,7 @@ test1379 test1380 test1381 test1382 test1383 test1384 test1385 test1386 \
test1387 test1388 test1389 test1390 test1391 test1392 test1393 \ test1387 test1388 test1389 test1390 test1391 test1392 test1393 \
test1400 test1401 test1402 test1403 test1404 test1405 test1406 test1407 \ test1400 test1401 test1402 test1403 test1404 test1405 test1406 test1407 \
test1408 test1409 test1410 test1411 test1412 test1413 \ test1408 test1409 test1410 test1411 test1412 test1413 \
test1500 test1501 test1502 test1503 test1504 test1505 test1506 \ test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \
test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \
test2016 test2017 test2018 test2019 test2020 test2021 test2022 \ test2016 test2017 test2018 test2019 test2020 test2021 test2022 \

51
tests/data/test1507 Normal file
View File

@ -0,0 +1,51 @@
<testcase>
<info>
<keywords>
SMTP
multi
</keywords>
</info>
#
# Server-side
<reply>
</reply>
#
# Client-side
<client>
<server>
smtp
</server>
<tool>
lib1507
</tool>
# based on bug report #1184
<name>
SMTP with multi interface and CURLE_ABORTED_BY_CALLBACK
</name>
<stdin>
From: different
To: another
body
</stdin>
<command>
smtp://%HOSTIP:%SMTPPORT/user
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<protocol>
EHLO user
MAIL FROM:<1507-realuser@example.com>
RCPT TO:<1507-recipient@example.com>
DATA
</protocol>
<upload>
</upload>
</verify>
</testcase>

View File

@ -23,7 +23,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib582 lib583 lib585 lib586 lib587 \ lib582 lib583 lib585 lib586 lib587 \
lib590 lib591 lib597 lib598 lib599 \ lib590 lib591 lib597 lib598 lib599 \
\ \
lib1500 lib1501 lib1502 lib1503 lib1504 lib1505 lib1506 lib1500 lib1501 lib1502 lib1503 lib1504 lib1505 lib1506 lib1507
chkhostname_SOURCES = chkhostname.c ../../lib/curl_gethostname.c chkhostname_SOURCES = chkhostname.c ../../lib/curl_gethostname.c
chkhostname_LDADD = @CURL_NETWORK_LIBS@ chkhostname_LDADD = @CURL_NETWORK_LIBS@
@ -312,3 +312,7 @@ lib1505_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1505
lib1506_SOURCES = lib1506.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1506_SOURCES = lib1506.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1506_LDADD = $(TESTUTIL_LIBS) lib1506_LDADD = $(TESTUTIL_LIBS)
lib1506_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1506 lib1506_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1506
lib1507_SOURCES = lib1507.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1507_LDADD = $(TESTUTIL_LIBS)
lib1507_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1507

167
tests/libtest/lib1507.c Normal file
View File

@ -0,0 +1,167 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2013, 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.
*
***************************************************************************/
#include "test.h"
#include "testutil.h"
#include "warnless.h"
#include "memdebug.h"
/*
* This is the list of basic details you need to tweak to get things right.
*/
#define USERNAME "user@example.com"
#define PASSWORD "123qwerty"
#define RECIPIENT "<1507-recipient@example.com>"
#define MAILFROM "<1507-realuser@example.com>"
#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
(void)ptr;
(void)size;
(void)nmemb;
(void)userp;
return CURL_READFUNC_ABORT;
}
static struct timeval tvnow(void)
{
/*
** time() returns the value of time in seconds since the Epoch.
*/
struct timeval now;
now.tv_sec = (long)time(NULL);
now.tv_usec = 0;
return now;
}
static long tvdiff(struct timeval newer, struct timeval older)
{
return (newer.tv_sec-older.tv_sec)*1000+
(newer.tv_usec-older.tv_usec)/1000;
}
int test(char *URL)
{
CURL *curl;
CURLM *mcurl;
int still_running = 1;
struct timeval mp_start;
struct curl_slist* rcpt_list = NULL;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(!curl)
return 1;
mcurl = curl_multi_init();
if(!mcurl)
return 2;
rcpt_list = curl_slist_append(rcpt_list, RECIPIENT);
/* more addresses can be added here
rcpt_list = curl_slist_append(rcpt_list, "<others@example.com>");
*/
curl_easy_setopt(curl, CURLOPT_URL, URL);
#if 0
curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
#endif
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MAILFROM);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_multi_add_handle(mcurl, curl);
mp_start = tvnow();
/* we start some action by calling perform right away */
curl_multi_perform(mcurl, &still_running);
while(still_running) {
struct timeval timeout;
int rc; /* select() return code */
fd_set fdread;
fd_set fdwrite;
fd_set fdexcep;
int maxfd = -1;
long curl_timeo = -1;
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep);
/* set a suitable timeout to play around with */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
curl_multi_timeout(mcurl, &curl_timeo);
if(curl_timeo >= 0) {
timeout.tv_sec = curl_timeo / 1000;
if(timeout.tv_sec > 1)
timeout.tv_sec = 1;
else
timeout.tv_usec = (curl_timeo % 1000) * 1000;
}
/* get file descriptors from the transfers */
curl_multi_fdset(mcurl, &fdread, &fdwrite, &fdexcep, &maxfd);
/* In a real-world program you OF COURSE check the return code of the
function calls. On success, the value of maxfd is guaranteed to be
greater or equal than -1. We call select(maxfd + 1, ...), specially in
case of (maxfd == -1), we call select(0, ...), which is basically equal
to sleep. */
rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
if (tvdiff(tvnow(), mp_start) > MULTI_PERFORM_HANG_TIMEOUT) {
fprintf(stderr, "ABORTING TEST, since it seems "
"that it would have run forever.\n");
break;
}
switch(rc) {
case -1:
/* select error */
break;
case 0: /* timeout */
default: /* action */
curl_multi_perform(mcurl, &still_running);
break;
}
}
curl_slist_free_all(rcpt_list);
curl_multi_remove_handle(mcurl, curl);
curl_multi_cleanup(mcurl);
curl_easy_cleanup(curl);
curl_global_cleanup();
return 0;
}