http: return error for a second Location: header

Unless it is identical to the previous one.

Follow-up to dbcaa00657

Adjusted test 580, added test 772 and 773

Fixes #19130
Reported-by: Jakub Stasiak
Closes #19134
This commit is contained in:
Daniel Stenberg 2025-10-19 10:59:38 +02:00
parent cbd7823fd1
commit 9596c4a258
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
5 changed files with 138 additions and 18 deletions

View file

@ -3265,9 +3265,7 @@ static CURLcode http_header_l(struct Curl_easy *data,
data->info.filetime = k->timeofdoc; data->info.filetime = k->timeofdoc;
return CURLE_OK; return CURLE_OK;
} }
if((k->httpcode >= 300 && k->httpcode < 400) && if(HD_IS(hd, hdlen, "Location:")) {
HD_IS(hd, hdlen, "Location:") &&
!data->req.location) {
/* this is the URL that the server advises us to use instead */ /* this is the URL that the server advises us to use instead */
char *location = Curl_copy_header_value(hd); char *location = Curl_copy_header_value(hd);
if(!location) if(!location)
@ -3276,23 +3274,33 @@ static CURLcode http_header_l(struct Curl_easy *data,
/* ignore empty data */ /* ignore empty data */
free(location); free(location);
else { else {
data->req.location = location; if(data->req.location &&
strcmp(data->req.location, location)) {
failf(data, "Multiple Location headers");
free(location);
return CURLE_WEIRD_SERVER_REPLY;
}
else {
free(data->req.location);
data->req.location = location;
if(data->set.http_follow_mode) { if((k->httpcode >= 300 && k->httpcode < 400) &&
CURLcode result; data->set.http_follow_mode) {
DEBUGASSERT(!data->req.newurl); CURLcode result;
data->req.newurl = strdup(data->req.location); /* clone */ DEBUGASSERT(!data->req.newurl);
if(!data->req.newurl) data->req.newurl = strdup(data->req.location); /* clone */
return CURLE_OUT_OF_MEMORY; if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
/* some cases of POST and PUT etc needs to rewind the data /* some cases of POST and PUT etc needs to rewind the data
stream at this point */ stream at this point */
result = http_perhapsrewind(data, conn); result = http_perhapsrewind(data, conn);
if(result) if(result)
return result; return result;
/* mark the next request as a followed location: */ /* mark the next request as a followed location: */
data->state.this_is_a_follow = TRUE; data->state.this_is_a_follow = TRUE;
}
} }
} }
} }

View file

@ -110,7 +110,7 @@ test736 test737 test738 test739 test740 test741 test742 test743 test744 \
test745 test746 test747 test748 test749 test750 test751 test752 test753 \ test745 test746 test747 test748 test749 test750 test751 test752 test753 \
test754 test755 test756 test757 test758 test759 test760 test761 test762 \ test754 test755 test756 test757 test758 test759 test760 test761 test762 \
test763 test764 test765 test766 test767 test768 test769 test770 test771 \ test763 test764 test765 test766 test767 test768 test769 test770 test771 \
\ test772 test773 \
test780 test781 test782 test783 test784 test785 test786 test787 test788 \ test780 test781 test782 test783 test784 test785 test786 test787 test788 \
test789 test790 test791 test792 test793 test794 test796 test797 \ test789 test790 test791 test792 test793 test794 test796 test797 \
\ \

View file

@ -20,6 +20,14 @@ Connection: close
Location: and there's a second one too! / moo.html Location: and there's a second one too! / moo.html
</data> </data>
<datacheck>
HTTP/1.1 302 eat this!
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Location: this-is-the-first.html
Content-Length: 0
Connection: close
</datacheck>
</reply> </reply>
# Client-side # Client-side
@ -51,5 +59,8 @@ Host: %HOSTIP:%HTTPPORT
Accept: */* Accept: */*
</protocol> </protocol>
<errorcode>
8
</errorcode>
</verify> </verify>
</testcase> </testcase>

52
tests/data/test772 Normal file
View file

@ -0,0 +1,52 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
Location
</keywords>
</info>
#
# Server-side
<reply>
<data crlf="yes" nocheck="yes">
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Content-Length: 0
Connection: close
Location: this
Location: that
</data>
</reply>
#
# Client-side
<client>
<server>
http
</server>
<name>
HTTP with two Location: headers triggers error
</name>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<protocol crlf="yes">
GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
</protocol>
<errorcode>
8
</errorcode>
</verify>
</testcase>

49
tests/data/test773 Normal file
View file

@ -0,0 +1,49 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
Location
</keywords>
</info>
#
# Server-side
<reply>
<data crlf="yes" nocheck="yes">
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Content-Length: 0
Connection: close
Location: this
Location: this
</data>
</reply>
#
# Client-side
<client>
<server>
http
</server>
<name>
HTTP with two identical Location: headers triggers no error
</name>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<protocol crlf="yes">
GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
</protocol>
</verify>
</testcase>