/*
* http_perhapsrewind()
*
- * If we are doing POST or PUT {
- * If we have more data to send {
- * If we are doing NTLM {
- * Keep sending since we must not disconnect
- * }
- * else {
- * If there is more than just a little data left to send, close
- * the current connection by force.
- * }
- * }
- * If we have sent any data {
- * If we don't have track of all the data {
- * call app to tell it to rewind
- * }
- * else {
- * rewind internally so that the operation can restart fine
- * }
- * }
- * }
+ * The current request needs to be done again - maybe due to a follow
+ * or authentication negotiation. Check if:
+ * 1) a rewind of the data sent to the server is necessary
+ * 2) the current transfer should continue or be stopped early
*/
static CURLcode http_perhapsrewind(struct Curl_easy *data,
struct connectdata *conn)
{
- struct HTTP *http = data->req.p.http;
- curl_off_t bytessent;
+ curl_off_t bytessent = data->req.writebytecount;
curl_off_t expectsend = Curl_creader_total_length(data);
+ curl_off_t upload_remain = (expectsend >= 0)? (expectsend - bytessent) : -1;
+ bool little_upload_remains = (upload_remain >= 0 && upload_remain < 2000);
+ bool needs_rewind = Curl_creader_needs_rewind(data);
+ /* By default, we'd like to abort the transfer when little or
+ * unknown amount remains. But this may be overridden by authentications
+ * further below! */
+ bool abort_upload = (!data->req.upload_done && !little_upload_remains);
+ const char *ongoing_auth = NULL;
+
+ /* We need a rewind before uploading client read data again. The
+ * checks below just influence of the upload is to be continued
+ * or aborted early.
+ * This depends on how much remains to be sent and in what state
+ * the authentication is. Some auth schemes such as NTLM do not work
+ * for a new connection. */
+ if(needs_rewind) {
+ infof(data, "Need to rewind upload for next request");
+ Curl_creader_set_rewind(data, TRUE);
+ }
- if(!http)
- /* If this is still NULL, we have not reach very far and we can safely
- skip this rewinding stuff */
- return CURLE_OK;
-
- if(!expectsend)
- /* not sending any body */
+ if(conn->bits.close)
+ /* If we already decided to close this connection, we cannot veto. */
return CURLE_OK;
- if(!conn->bits.protoconnstart)
- /* HTTP CONNECT in progress: there is no body */
- expectsend = 0;
-
- bytessent = data->req.writebytecount;
- Curl_creader_set_rewind(data, FALSE);
-
- if((expectsend == -1) || (expectsend > bytessent)) {
+ if(abort_upload) {
+ /* We'd like to abort the upload - but should we? */
#if defined(USE_NTLM)
- /* There is still data left to send */
if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
(data->state.authhost.picked == CURLAUTH_NTLM) ||
(data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
(data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
- if(((expectsend - bytessent) < 2000) ||
- (conn->http_ntlm_state != NTLMSTATE_NONE) ||
+ ongoing_auth = "NTML";
+ if((conn->http_ntlm_state != NTLMSTATE_NONE) ||
(conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
- /* The NTLM-negotiation has started *OR* there is just a little (<2K)
- data left to send, keep on sending. */
-
- /* rewind data when completely done sending! */
- if(!data->req.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
- Curl_creader_set_rewind(data, TRUE);
- infof(data, "Rewind stream before next send");
- }
-
- return CURLE_OK;
+ /* The NTLM-negotiation has started, keep on sending.
+ * Need to do further work on same connection */
+ abort_upload = FALSE;
}
-
- if(conn->bits.close)
- /* this is already marked to get closed */
- return CURLE_OK;
-
- infof(data, "NTLM send, close instead of sending %"
- CURL_FORMAT_CURL_OFF_T " bytes",
- (curl_off_t)(expectsend - bytessent));
}
#endif
#if defined(USE_SPNEGO)
/* There is still data left to send */
if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) ||
(data->state.authhost.picked == CURLAUTH_NEGOTIATE)) {
- if(((expectsend - bytessent) < 2000) ||
- (conn->http_negotiate_state != GSS_AUTHNONE) ||
+ ongoing_auth = "NEGOTIATE";
+ if((conn->http_negotiate_state != GSS_AUTHNONE) ||
(conn->proxy_negotiate_state != GSS_AUTHNONE)) {
- /* The NEGOTIATE-negotiation has started *OR*
- there is just a little (<2K) data left to send, keep on sending. */
-
- /* rewind data when completely done sending! */
- if(!data->req.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
- Curl_creader_set_rewind(data, TRUE);
- infof(data, "Rewind stream before next send");
- }
-
- return CURLE_OK;
+ /* The NEGOTIATE-negotiation has started, keep on sending.
+ * Need to do further work on same connection */
+ abort_upload = FALSE;
}
-
- if(conn->bits.close)
- /* this is already marked to get closed */
- return CURLE_OK;
-
- infof(data, "NEGOTIATE send, close instead of sending %"
- CURL_FORMAT_CURL_OFF_T " bytes",
- (curl_off_t)(expectsend - bytessent));
}
#endif
+ }
- /* This is not NEGOTIATE/NTLM or many bytes left to send: close */
+ if(abort_upload) {
+ if(upload_remain >= 0)
+ infof(data, "%s%sclose instead of sending %"
+ CURL_FORMAT_CURL_OFF_T " more bytes",
+ ongoing_auth? ongoing_auth : "",
+ ongoing_auth? " send, " : "",
+ upload_remain);
+ else
+ infof(data, "%s%sclose instead of sending unknown amount "
+ "of more bytes",
+ ongoing_auth? ongoing_auth : "",
+ ongoing_auth? " send, " : "");
+ /* We decided to abort the ongoing transfer */
streamclose(conn, "Mid-auth HTTP and much data left to send");
+ /* FIXME: questionable manipulation here, can we do this differently? */
data->req.size = 0; /* don't download any more than 0 bytes */
-
- /* There still is data left to send, but this connection is marked for
- closure so we can safely do the rewind right now */
- }
-
- if(Curl_creader_needs_rewind(data)) {
- /* mark for rewind since if we already sent something */
- Curl_creader_set_rewind(data, TRUE);
- infof(data, "Please rewind output before next send");
}
-
return CURLE_OK;
}
#endif
if(pickhost || pickproxy) {
- if((data->state.httpreq != HTTPREQ_GET) &&
- (data->state.httpreq != HTTPREQ_HEAD) &&
- !Curl_creader_will_rewind(data)) {
- result = http_perhapsrewind(data, conn);
- if(result)
- return result;
- }
+ result = http_perhapsrewind(data, conn);
+ if(result)
+ return result;
+
/* In case this is GSS auth, the newurl field is already allocated so
we must make sure to free it before allocating a new one. As figured
out in bug #2284386 */