transfer: fix retry for empty downloads on reuse
No binary (build failed)
—
(log yok)
| @@ -254,6 +254,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, | |
| curl_off_t total_received = 0; | |
| bool is_multiplex = FALSE; | |
| bool rcvd_eagain = FALSE; | |
| + | bool is_eos = FALSE; |
| result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); | |
| if(result) | |
| @@ -262,7 +263,6 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, | |
| /* This is where we loop until we have read everything there is to | |
| read or we get a CURLE_AGAIN */ | |
| do { | |
| - | bool is_eos = FALSE; |
| size_t bytestoread; | |
| ssize_t nread; | |
| @@ -309,9 +309,14 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, | |
| blen = (size_t)nread; | |
| is_eos = (blen == 0); | |
| - | if(!blen) { |
| - | /* if we receive 0 or less here, either the data transfer is done or the |
| - | server closed the connection and we bail out from this! */ |
| + | if(!blen && (conn->recv[FIRSTSOCKET] == Curl_cf_recv)) { |
| + | /* if we receive 0 or less here and the protocol handler did not |
| + | replace the connection's `recv` callback, either the data transfer |
| + | is done or the server closed the connection and |
| + | we bail out from this! |
| + | With a `recv` replacement, we assume the protocol handler knows |
| + | what it is doing and a 0-length receive is fine. For example, |
| + | SFTP downloads of an empty file would show this. See #19165. */ |
| if(is_multiplex) | |
| DEBUGF(infof(data, "nread == 0, stream closed, bailing")); | |
| else | |
| @@ -340,9 +345,9 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, | |
| } while(maxloops--); | |
| - | if(!Curl_xfer_is_blocked(data) && |
| + | if(!is_eos && !Curl_xfer_is_blocked(data) && |
| (!rcvd_eagain || data_pending(data, rcvd_eagain))) { | |
| - | /* Did not read until EAGAIN or there is still data pending |
| + | /* Did not read until EAGAIN/EOS or there is still data pending |
| * in buffers. Mark as read-again via simulated SELECT results. */ | |
| Curl_multi_mark_dirty(data); | |
| CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty"); | |
| @@ -662,9 +667,10 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) | |
| !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP))) | |
| return CURLE_OK; | |
| - | if((data->req.bytecount + data->req.headerbytecount == 0) && |
| - | conn->bits.reuse && |
| - | (!data->req.no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP)) |
| + | if(conn->bits.reuse && |
| + | (data->req.bytecount + data->req.headerbytecount == 0) && |
| + | ((!data->req.no_body && !data->req.done) || |
| + | (conn->handler->protocol & PROTO_FAMILY_HTTP)) |
| #ifndef CURL_DISABLE_RTSP | |
| && (data->set.rtspreq != RTSPREQ_RECEIVE) | |
| #endif | |
| @@ -3837,18 +3837,16 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, | |
| struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); | |
| (void)dead_connection; | |
| - | DEBUGF(infof(data, "SSH DISCONNECT starts now")); |
| - | |
| - | if(sshc && sshc->ssh_session && sshp) { |
| - | /* only if there is a session still around to use! */ |
| - | myssh_state(data, sshc, SSH_SFTP_SHUTDOWN); |
| - | result = ssh_block_statemach(data, sshc, sshp, TRUE); |
| - | } |
| - | |
| - | DEBUGF(infof(data, "SSH DISCONNECT is done")); |
| - | if(sshc) |
| + | if(sshc) { |
| + | if(sshc->ssh_session && sshp) { |
| + | /* only if there is a session still around to use! */ |
| + | DEBUGF(infof(data, "SSH DISCONNECT starts now")); |
| + | myssh_state(data, sshc, SSH_SFTP_SHUTDOWN); |
| + | result = ssh_block_statemach(data, sshc, sshp, TRUE); |
| + | DEBUGF(infof(data, "SSH DISCONNECT is done -> %d", result)); |
| + | } |
| sshc_cleanup(sshc, data, TRUE); | |
| - | |
| + | } |
| return result; | |
| } | |
| @@ -209,7 +209,7 @@ test1548 test1549 test1550 test1551 test1552 test1553 test1554 test1555 \ | |
| test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 \ | |
| test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 \ | |
| test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 \ | |
| - | test1580 test1581 test1582 \ |
| + | test1580 test1581 test1582 test1583 \ |
| \ | |
| test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 \ | |
| test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 \ | |
| @@ -0,0 +1,37 @@ | |
| + | <testcase> |
| + | <info> |
| + | <keywords> |
| + | SFTP |
| + | </keywords> |
| + | </info> |
| + | |
| + | # |
| + | # Server-side |
| + | <reply> |
| + | </reply> |
| + | |
| + | # |
| + | # Client-side |
| + | <client> |
| + | <server> |
| + | sftp |
| + | </server> |
| + | <precheck> |
| + | %PERL %SRCDIR/libtest/test613.pl prepare %PWD/%LOGDIR/test%TESTNUMBER.dir |
| + | </precheck> |
| + | <name> |
| + | SFTP dir and empty file |
| + | </name> |
| + | <command> |
| + | --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: --insecure sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/test%TESTNUMBER.dir/ --next --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: --insecure sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/test%TESTNUMBER.dir/emptyfile.txt |
| + | </command> |
| + | </client> |
| + | |
| + | # |
| + | # Verify data after the test has been "shot" |
| + | <verify> |
| + | <errorcode> |
| + | 0 |
| + | </errorcode> |
| + | </verify> |
| + | </testcase> |
| @@ -11,6 +11,7 @@ directory | |
| <reply> | |
| <datacheck> | |
| d????????? N U U N ??? N NN:NN asubdir | |
| + | -rw??????? 1 U U 0 Jan 1 2000 emptyfile.txt |
| -rw??????? 1 U U 37 Jan 1 2000 plainfile.txt | |
| -r-??????? 1 U U 47 Dec 31 2000 rofile.txt | |
| </datacheck> | |
| @@ -12,6 +12,7 @@ directory | |
| <reply> | |
| <datacheck> | |
| d????????? N U U N ??? N NN:NN asubdir | |
| + | -rw??????? 1 U U 0 Jan 1 2000 emptyfile.txt |
| -r-??????? 1 U U 37 Jan 1 2000 plainfile.txt | |
| -r-??????? 1 U U 47 Dec 31 2000 rofile.txt | |
| </datacheck> | |
| @@ -59,6 +59,14 @@ if($ARGV[0] eq "prepare") { | |
| utime time, timegm(0,0,12,1,0,100), "plainfile.txt"; | |
| chmod 0666, "plainfile.txt"; | |
| + | open(FILE, ">emptyfile.txt") || errout "$!"; |
| + | binmode FILE; |
| + | close(FILE); |
| + | # The mtime is specifically chosen to be an even number so that it can be |
| + | # represented exactly on a FAT file system. |
| + | utime time, timegm(0,0,12,1,0,100), "emptyfile.txt"; |
| + | chmod 0666, "emptyfile.txt"; |
| + | |
| open(FILE, ">rofile.txt") || errout "$!"; | |
| binmode FILE; | |
| print FILE "Read-only test file to support curl test suite\n"; | |
| @@ -83,6 +91,7 @@ elsif($ARGV[0] eq "postprocess") { | |
| } | |
| chmod 0666, "$dirname/rofile.txt"; | |
| unlink "$dirname/rofile.txt"; | |
| + | unlink "$dirname/emptyfile.txt"; |
| unlink "$dirname/plainfile.txt"; | |
| rmdir "$dirname/asubdir"; | |