mirror of
https://github.com/curl/curl.git
synced 2026-08-24 16:03:37 +03:00
lib: rewind BEFORE request instead of AFTER previous
This makes a big difference for cases when the rewind is not actually necessary to perofm (for example HTTP response code 301 converts to GET) and therefore the rewind can be avoided. In particular for situations when that rewind fails, for example when reading from a pipe or similar. Reported-by: Ali Utku Selen Fixes #9735 Closes #9958
This commit is contained in:
parent
9ecebb5d00
commit
1b3973180b
7 changed files with 115 additions and 121 deletions
93
lib/multi.c
93
lib/multi.c
|
|
@ -1729,6 +1729,90 @@ static CURLcode protocol_connect(struct Curl_easy *data,
|
|||
return result; /* pass back status */
|
||||
}
|
||||
|
||||
/*
|
||||
* readrewind() rewinds the read stream. This is typically used for HTTP
|
||||
* POST/PUT with multi-pass authentication when a sending was denied and a
|
||||
* resend is necessary.
|
||||
*/
|
||||
static CURLcode readrewind(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
curl_mimepart *mimepart = &data->set.mimepost;
|
||||
DEBUGASSERT(conn);
|
||||
|
||||
data->state.rewindbeforesend = FALSE; /* we rewind now */
|
||||
|
||||
/* explicitly switch off sending data on this connection now since we are
|
||||
about to restart a new transfer and thus we want to avoid inadvertently
|
||||
sending more data on the existing connection until the next transfer
|
||||
starts */
|
||||
data->req.keepon &= ~KEEP_SEND;
|
||||
|
||||
/* We have sent away data. If not using CURLOPT_POSTFIELDS or
|
||||
CURLOPT_HTTPPOST, call app to rewind
|
||||
*/
|
||||
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
|
||||
struct HTTP *http = data->req.p.http;
|
||||
|
||||
if(http->sendit)
|
||||
mimepart = http->sendit;
|
||||
}
|
||||
if(data->set.postfields ||
|
||||
(data->state.httpreq == HTTPREQ_GET) ||
|
||||
(data->state.httpreq == HTTPREQ_HEAD))
|
||||
; /* no need to rewind */
|
||||
else if(data->state.httpreq == HTTPREQ_POST_MIME ||
|
||||
data->state.httpreq == HTTPREQ_POST_FORM) {
|
||||
CURLcode result = Curl_mime_rewind(mimepart);
|
||||
if(result) {
|
||||
failf(data, "Cannot rewind mime/post data");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(data->set.seek_func) {
|
||||
int err;
|
||||
|
||||
Curl_set_in_callback(data, true);
|
||||
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
|
||||
Curl_set_in_callback(data, false);
|
||||
if(err) {
|
||||
failf(data, "seek callback returned error %d", (int)err);
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
else if(data->set.ioctl_func) {
|
||||
curlioerr err;
|
||||
|
||||
Curl_set_in_callback(data, true);
|
||||
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
|
||||
data->set.ioctl_client);
|
||||
Curl_set_in_callback(data, false);
|
||||
infof(data, "the ioctl callback returned %d", (int)err);
|
||||
|
||||
if(err) {
|
||||
failf(data, "ioctl callback returned error %d", (int)err);
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If no CURLOPT_READFUNCTION is used, we know that we operate on a
|
||||
given FILE * stream and we can actually attempt to rewind that
|
||||
ourselves with fseek() */
|
||||
if(data->state.fread_func == (curl_read_callback)fread) {
|
||||
if(-1 != fseek(data->state.in, 0, SEEK_SET))
|
||||
/* successful rewind */
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* no callback set or failure above, makes us fail at once */
|
||||
failf(data, "necessary data rewind wasn't possible");
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_preconnect() is called immediately before a connect starts. When a
|
||||
* redirect is followed, this is then called multiple times during a single
|
||||
|
|
@ -1741,6 +1825,7 @@ CURLcode Curl_preconnect(struct Curl_easy *data)
|
|||
if(!data->state.buffer)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
@ -1997,7 +2082,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
break;
|
||||
|
||||
case MSTATE_PROTOCONNECT:
|
||||
if(data->conn->bits.reuse) {
|
||||
if(data->state.rewindbeforesend)
|
||||
result = readrewind(data);
|
||||
|
||||
if(!result && data->conn->bits.reuse) {
|
||||
/* ftp seems to hang when protoconnect on reused connection
|
||||
* since we handle PROTOCONNECT in general inside the filers, it
|
||||
* seems wrong to restart this on a reused connection. */
|
||||
|
|
@ -2005,7 +2093,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
rc = CURLM_CALL_MULTI_PERFORM;
|
||||
break;
|
||||
}
|
||||
result = protocol_connect(data, &protocol_connected);
|
||||
if(!result)
|
||||
result = protocol_connect(data, &protocol_connected);
|
||||
if(!result && !protocol_connected)
|
||||
/* switch to waiting state */
|
||||
multistate(data, MSTATE_PROTOCONNECTING);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue