transfer: fix CURLOPT_CURLU override logic

- Change setopt and pretransfer to always reset URL related variables
  for a CURLU handle set CURLOPT_CURLU.

This change is to ensure we are in compliance with the doc which says
CURLU handles must be able to override a URL set via CURLOPT_URL and
that if the contents of the CURLU handle changes between transfers then
the updated contents must be used.

Prior to this change, although subsequent transfers appear to be
performed correctly in those cases, the work URL `data->state.url` was
not updated. CURLINFO_EFFECTIVE_URL returns data->state.url to the user
so it would return the URL from the initial transfer which was the wrong
URL. It's likely there are other cases as well.

Ref: https://curl.se/libcurl/c/CURLOPT_CURLU.html

Reported-by: Nicolás San Martín

Fixes https://github.com/curl/curl/issues/15984
Closes https://github.com/curl/curl/pull/15985
This commit is contained in:
Jay Satiro 2025-01-13 03:57:45 -05:00
parent 8ab468c8aa
commit 5ffc73c78e
6 changed files with 182 additions and 13 deletions

View file

@ -528,20 +528,15 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
if(!data->state.url && !data->set.uh) {
if(!data->set.str[STRING_SET_URL] && !data->set.uh) {
/* we cannot do anything without URL */
failf(data, "No URL set");
return CURLE_URL_MALFORMAT;
}
/* since the URL may have been redirected in a previous use of this handle */
if(data->state.url_alloc) {
/* the already set URL is allocated, free it first! */
Curl_safefree(data->state.url);
data->state.url_alloc = FALSE;
}
if(!data->state.url && data->set.uh) {
/* CURLOPT_CURLU overrides CURLOPT_URL and the contents of the CURLU handle
is allowed to be changed by the user between transfers */
if(data->set.uh) {
CURLUcode uc;
free(data->set.str[STRING_SET_URL]);
uc = curl_url_get(data->set.uh,
@ -552,6 +547,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
}
/* since the URL may have been redirected in a previous use of this handle */
if(data->state.url_alloc) {
Curl_safefree(data->state.url);
data->state.url_alloc = FALSE;
}
data->state.url = data->set.str[STRING_SET_URL];
if(data->set.postfields && data->set.set_resume_from) {
/* we cannot */
failf(data, "cannot mix POSTFIELDS with RESUME_FROM");
@ -563,7 +566,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.list_only = data->set.list_only;
#endif
data->state.httpreq = data->set.method;
data->state.url = data->set.str[STRING_SET_URL];
#ifdef USE_SSL
if(!data->state.ssl_scache)