From: Daniel Stenberg Date: Mon, 3 Mar 2025 10:35:48 +0000 (+0100) Subject: lib: add CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY X-Git-Tag: curl-8_13_0~296 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fb13923dd67d5196c47e8df9c05b5cca59c1a750;p=thirdparty%2Fcurl.git lib: add CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY With this change, the argument passed to the CURLOPT_FOLLOWLOCATION option is now instead a "mode" instead of just a boolean. Documentation is extended to describe the two new modes. Test 1571 to 1581 verify. Closes #16473 --- diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS index 0ae0af1c4e..98d6ff4597 100644 --- a/docs/KNOWN_BUGS +++ b/docs/KNOWN_BUGS @@ -602,7 +602,7 @@ problems may have been fixed or changed somewhat since this was written. 16.3 aws-sigv4 has problems with particular URLs - https://github.com/curl/curl/issues/13058 + https://github.com/curl/curl/issues/13085 16.6 aws-sigv4 does not behave well with AWS VPC Lattice diff --git a/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md b/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md index 9276a824ce..060cfd9c77 100644 --- a/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md +++ b/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md @@ -66,9 +66,15 @@ Many people have wrongly used this option to replace the entire request with their own, including multiple headers and POST contents. While that might work in many cases, it might cause libcurl to send invalid requests and it could possibly confuse the remote server badly. Use CURLOPT_POST(3) and -CURLOPT_POSTFIELDS(3) to set POST data. Use CURLOPT_HTTPHEADER(3) -to replace or extend the set of headers sent by libcurl. Use -CURLOPT_HTTP_VERSION(3) to change HTTP version. +CURLOPT_POSTFIELDS(3) to set POST data. Use CURLOPT_HTTPHEADER(3) to replace +or extend the set of headers sent by libcurl. Use CURLOPT_HTTP_VERSION(3) to +change the HTTP version. + +When this option is used together with CURLOPT_FOLLOWLOCATION(3), the custom +set method overrides the method libcurl could otherwise change to for the +subsequent requests. You can fine-tune that decision by using the +CURLFOLLOW_OBEYCODE bit to CURLOPT_FOLLOWLOCATION(3) to make redirects adhere +to the redirect response code as the protocol instructs. ## FTP diff --git a/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md b/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md index 026fb35528..af54e17e13 100644 --- a/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md +++ b/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md @@ -25,19 +25,23 @@ CURLOPT_FOLLOWLOCATION - follow HTTP 3xx redirects ~~~c #include -CURLcode curl_easy_setopt(CURL *handle, CURLOPT_FOLLOWLOCATION, long enable); +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_FOLLOWLOCATION, long mode); ~~~ # DESCRIPTION -A long parameter set to 1 tells the library to follow any Location: header -redirects that an HTTP server sends in a 30x response. The Location: header -can specify a relative or an absolute URL to follow. +This option tells the library to follow `Location:` header redirects that an +HTTP server sends in a 30x response. The `Location:` header can specify a +relative or an absolute URL to follow. The long parameter *mode* instructs how +libcurl should act on subsequent requests. -libcurl issues another request for the new URL and follows subsequent new -`Location:` redirects all the way until no more such headers are returned or -the maximum limit is reached. CURLOPT_MAXREDIRS(3) is used to limit the number -of redirects libcurl follows. +*mode* only had a single value (1L) for a long time that enables redirect +following. Since 8.13.0, two additional modes are also supported. See below. + +When following redirects, libcurl issues another request for the new URL and +follows subsequent new `Location:` redirects all the way until no more such +headers are returned or the maximum limit is reached. CURLOPT_MAXREDIRS(3) is +used to limit the number of redirects libcurl follows. libcurl restricts what protocols it automatically follow redirects to. The accepted target protocols are set with CURLOPT_REDIR_PROTOCOLS_STR(3). By @@ -64,6 +68,45 @@ client may not want to pass on to other servers than the initially intended host and for all other headers than the two mentioned above, there is no protection from this happening when libcurl is told to follow redirects. +Pick one of the following modes: + +## CURLFOLLOW_ALL (1) + +Before 8.13.0 this bit had no name and 1L was just the value to enable this +option. This makes a set custom method be used in all HTTP requests, even +after redirects. + +## CURLFOLLOW_OBEYCODE (2) + +When there is a custom request method set with CURLOPT_CUSTOMREQUEST(3), that +set method replaces what libcurl would otherwise use. If a 301/302/303 +response code is returned to signal a redirect, the method is changed from +POST to `GET`. For 307/308, the custom method remains set and used. + +Note that only POST (or a custom post) is changed to GET on 301/302, its not +change PUT etc - and therefore also not when libcurl issues a custom PUT. A +303 response makes it switch to GET independently of the original method +(except for HEAD). + +To control for which of the 301/302/303 status codes libcurl should *not* +switch back to GET for when doing a custom POST, and instead keep the custom +method, use CURLOPT_POSTREDIR(3). + +If you prefer a custom POST method to be reset to exactly the method `POST`, +use CURLFOLLOW_FIRSTONLY instead. + +## CURLFOLLOW_FIRSTONLY (3) + +When there is a custom request method set with CURLOPT_CUSTOMREQUEST(3), that +set method replaces what libcurl would otherwise use in the first outgoing +request only. The second request is then done according to the redirect +response code. + +If you prefer your custom method to remain in use after a 307/308 redirect, +use CURLFOLLOW_OBEYCODE instead. + +## + # NOTE Since libcurl changes method or not based on the specific HTTP response code, @@ -72,6 +115,10 @@ libcurl would otherwise do and if not that carefully may even make it misbehave since CURLOPT_CUSTOMREQUEST(3) overrides the method libcurl would otherwise select internally. +Setting the CURLFOLLOW_OBEYCODE bit makes libcurl *not* use the custom set +method after redirects for 301, 302 and 303 responses. Unless the +CURLOPT_POSTREDIR(3) bits are set for those status codes. + # DEFAULT 0, disabled diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 0b566edbc3..39771cafc9 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -337,11 +337,11 @@ CURLE_UNRECOVERABLE_POLL 7.84.0 CURLE_UNSUPPORTED_PROTOCOL 7.1 CURLE_UPLOAD_FAILED 7.16.3 CURLE_URL_MALFORMAT 7.1 +CURLE_ECH_REQUIRED 8.8.0 CURLE_URL_MALFORMAT_USER 7.1 7.17.0 CURLE_USE_SSL_FAILED 7.17.0 CURLE_WEIRD_SERVER_REPLY 7.51.0 CURLE_WRITE_ERROR 7.1 -CURLE_ECH_REQUIRED 8.8.0 CURLFILETYPE_DEVICE_BLOCK 7.21.0 CURLFILETYPE_DEVICE_CHAR 7.21.0 CURLFILETYPE_DIRECTORY 7.21.0 @@ -359,6 +359,9 @@ CURLFINFOFLAG_KNOWN_PERM 7.21.0 CURLFINFOFLAG_KNOWN_SIZE 7.21.0 CURLFINFOFLAG_KNOWN_TIME 7.21.0 CURLFINFOFLAG_KNOWN_UID 7.21.0 +CURLFOLLOW_ALL 8.13.0 +CURLFOLLOW_OBEYCODE 8.13.0 +CURLFOLLOW_FIRSTONLY 8.13.0 CURLFORM_ARRAY 7.9.1 7.56.0 CURLFORM_ARRAY_END 7.9.1 7.9.5 7.9.6 CURLFORM_ARRAY_START 7.9.1 7.9.5 7.9.6 diff --git a/include/curl/curl.h b/include/curl/curl.h index 346872b39d..0d86ddac9b 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -175,6 +175,16 @@ typedef enum { #define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL #define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT +/* bits for the CURLOPT_FOLLOWLOCATION option */ +#define CURLFOLLOW_ALL 1L /* generic follow redirects */ + +/* Do not use the custom method in the follow-up request if the HTTP code + instructs so (301, 302, 303). */ +#define CURLFOLLOW_OBEYCODE 2L + +/* Only use the custom method in the first request, always reset in the next */ +#define CURLFOLLOW_FIRSTONLY 3L + struct curl_httppost { struct curl_httppost *next; /* next entry in the list */ char *name; /* pointer to allocated name */ diff --git a/lib/http.c b/lib/http.c index 944c35d63c..af05cf6bd0 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1154,6 +1154,21 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) return data->state.authproblem; } +static void http_switch_to_get(struct Curl_easy *data, int code) +{ + const char *req = data->set.str[STRING_CUSTOMREQUEST]; + if((req || data->state.httpreq != HTTPREQ_GET) && + (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) { + infof(data, "Switch to GET because of %d response", code); + data->state.http_ignorecustom = TRUE; + } + else if(req && (data->set.http_follow_mode != CURLFOLLOW_FIRSTONLY)) + infof(data, "Stick to %s instead of GET", req); + + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); +} + CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, followtype type) { @@ -1320,6 +1335,12 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, data->state.url_alloc = TRUE; Curl_req_soft_reset(&data->req, data); infof(data, "Issue another request to this URL: '%s'", data->state.url); + if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) && + data->set.str[STRING_CUSTOMREQUEST] && + !data->state.http_ignorecustom) { + data->state.http_ignorecustom = TRUE; + infof(data, "Drop custom request method for next request"); + } /* * We get here when the HTTP code is 300-399 (and 401). We need to perform @@ -1361,11 +1382,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, if((data->state.httpreq == HTTPREQ_POST || data->state.httpreq == HTTPREQ_POST_FORM || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_301)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - Curl_creader_set_rewind(data, FALSE); - } + && !(data->set.keep_post & CURL_REDIR_POST_301)) + http_switch_to_get(data, 301); break; case 302: /* Found */ /* (quote from RFC7231, section 6.4.3) @@ -1387,11 +1405,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, if((data->state.httpreq == HTTPREQ_POST || data->state.httpreq == HTTPREQ_POST_FORM || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_302)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - Curl_creader_set_rewind(data, FALSE); - } + && !(data->set.keep_post & CURL_REDIR_POST_302)) + http_switch_to_get(data, 302); break; case 303: /* See Other */ @@ -1404,11 +1419,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, ((data->state.httpreq != HTTPREQ_POST && data->state.httpreq != HTTPREQ_POST_FORM && data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { - data->state.httpreq = HTTPREQ_GET; - infof(data, "Switch to %s", - data->req.no_body ? "HEAD" : "GET"); - } + !(data->set.keep_post & CURL_REDIR_POST_303))) + http_switch_to_get(data, 303); break; case 304: /* Not Modified */ /* 304 means we did a conditional request and it was "Not modified". @@ -1802,8 +1814,10 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, httpreq = HTTPREQ_PUT; /* Now set the 'request' pointer to the proper request string */ - if(data->set.str[STRING_CUSTOMREQUEST]) + if(data->set.str[STRING_CUSTOMREQUEST] && + !data->state.http_ignorecustom) { request = data->set.str[STRING_CUSTOMREQUEST]; + } else { if(data->req.no_body) request = "HEAD"; @@ -3137,7 +3151,7 @@ static CURLcode http_header(struct Curl_easy *data, else { data->req.location = location; - if(data->set.http_follow_location) { + if(data->set.http_follow_mode) { DEBUGASSERT(!data->req.newurl); data->req.newurl = strdup(data->req.location); /* clone */ if(!data->req.newurl) diff --git a/lib/setopt.c b/lib/setopt.c index ef05cfb6ef..e3f944bdd3 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -525,7 +525,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, /* * Follow Location: header hints on an HTTP-server. */ - data->set.http_follow_location = enabled; + if(uarg > 3) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.http_follow_mode = (unsigned char)uarg; break; case CURLOPT_UNRESTRICTED_AUTH: diff --git a/lib/urldata.h b/lib/urldata.h index e04200d2e7..e944a95c07 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1358,6 +1358,7 @@ struct UrlState { BIT(internal); /* internal: true if this easy handle was created for internal use and the user does not have ownership of the handle. */ + BIT(http_ignorecustom); /* ignore custom method from now */ }; /* @@ -1727,6 +1728,7 @@ struct UserDefined { CURLOPT_GSSAPI_DELEGATION */ unsigned char gssapi_delegation; #endif + unsigned char http_follow_mode; /* follow HTTP redirects */ BIT(connect_only); /* make connection/request, then let application use the socket */ BIT(connect_only_ws); /* special websocket connect-only level */ @@ -1778,7 +1780,6 @@ struct UserDefined { BIT(hide_progress); /* do not use the progress meter */ BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */ BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */ - BIT(http_follow_location); /* follow HTTP redirects */ BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */ BIT(allow_auth_to_other_hosts); BIT(include_header); /* include received protocol headers in data output */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 015d568c53..0f4e2c1386 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -207,7 +207,8 @@ test1540 test1541 test1542 test1543 test1544 test1545 test1546 \ \ test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \ test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 \ -test1566 test1567 test1568 test1569 test1570 \ +test1566 test1567 test1568 test1569 test1570 test1571 test1572 test1573 \ +test1574 test1575 test1576 test1577 test1578 test1579 test1580 test1581 \ \ test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 \ test1598 \ diff --git a/tests/data/test1571 b/tests/data/test1571 new file mode 100644 index 0000000000..957dbded9b --- /dev/null +++ b/tests/data/test1571 @@ -0,0 +1,95 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib%TESTNUMBER + + + +CURLFOLLOW_OBEYCODE with custom POST method, 302 => GET + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1571 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooGET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + + diff --git a/tests/data/test1572 b/tests/data/test1572 new file mode 100644 index 0000000000..cb06637f7f --- /dev/null +++ b/tests/data/test1572 @@ -0,0 +1,97 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1571 + + + +CURLFOLLOW_OBEYCODE with custom POST method, 308 => custom + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1571 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooIGLOO /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +moo + + + diff --git a/tests/data/test1573 b/tests/data/test1573 new file mode 100644 index 0000000000..f0d8889c00 --- /dev/null +++ b/tests/data/test1573 @@ -0,0 +1,92 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1571 + + + +CURLFOLLOW_OBEYCODE with custom GET method, 301 => custom + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1573 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +IGLOO /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + diff --git a/tests/data/test1574 b/tests/data/test1574 new file mode 100644 index 0000000000..30b1a92633 --- /dev/null +++ b/tests/data/test1574 @@ -0,0 +1,92 @@ + + + +CURLFOLLOW_FIRSTONLY +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1571 + + + +CURLFOLLOW_FIRSTONLY with custom GET method, 301 => GET + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1574 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +GET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + diff --git a/tests/data/test1575 b/tests/data/test1575 new file mode 100644 index 0000000000..300856e6a3 --- /dev/null +++ b/tests/data/test1575 @@ -0,0 +1,97 @@ + + + +CURLFOLLOW_FIRSTONLY +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1571 + + + +CURLFOLLOW_FIRSTONLY with custom POST method, 308 => POST + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1575 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooPOST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +moo + + + diff --git a/tests/data/test1576 b/tests/data/test1576 new file mode 100644 index 0000000000..e2a34721c4 --- /dev/null +++ b/tests/data/test1576 @@ -0,0 +1,95 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib%TESTNUMBER + + + +CURLFOLLOW_OBEYCODE with custom PUT method, 302 => custom + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1576 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +CURL /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which madeCURL /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which made + + + diff --git a/tests/data/test1577 b/tests/data/test1577 new file mode 100644 index 0000000000..13a0bc3e2e --- /dev/null +++ b/tests/data/test1577 @@ -0,0 +1,95 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1576 + + + +CURLFOLLOW_OBEYCODE with custom PUT method, 308 => custom + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER %TESTNUMBER + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +CURL /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which madeCURL /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which made + + + diff --git a/tests/data/test1578 b/tests/data/test1578 new file mode 100644 index 0000000000..1518b3cb57 --- /dev/null +++ b/tests/data/test1578 @@ -0,0 +1,95 @@ + + + +CURLFOLLOW_FIRSTONLY +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1576 + + + +CURLFOLLOW_FIRSTONLY with custom PUT method, 302 => PUT + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER %TESTNUMBER + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +CURL /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which madePUT /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which made + + + diff --git a/tests/data/test1579 b/tests/data/test1579 new file mode 100644 index 0000000000..3214e192a3 --- /dev/null +++ b/tests/data/test1579 @@ -0,0 +1,93 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 303 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 303 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1576 + + + +CURLFOLLOW_OBEYCODE with custom PUT method, 303 => GET + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER %TESTNUMBER + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +CURL /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which madeGET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + diff --git a/tests/data/test1580 b/tests/data/test1580 new file mode 100644 index 0000000000..08295cda5c --- /dev/null +++ b/tests/data/test1580 @@ -0,0 +1,95 @@ + + + +CURLFOLLOW_FIRSTONLY +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1576 + + + +CURLFOLLOW_FIRSTONLY with custom PUT method, 308 => PUT + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1578 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +CURL /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which madePUT /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 45 + +request indicates that the client, which made + + + diff --git a/tests/data/test1581 b/tests/data/test1581 new file mode 100644 index 0000000000..2979e5f075 --- /dev/null +++ b/tests/data/test1581 @@ -0,0 +1,97 @@ + + + +CURLFOLLOW_OBEYCODE +CURLOPT_FOLLOWLOCATION + + +# +# Server-side + + +HTTP/1.1 301 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 301 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + +lib1571 + + + +CURLFOLLOW_OBEYCODE with custom POST301 method, 301 => custom + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1581 + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooIGLOO /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +moo + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index cf4d6e7122..12cdeb6aea 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -60,7 +60,8 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ lib1534 lib1535 lib1536 lib1537 lib1538 lib1539 \ lib1540 lib1541 lib1542 lib1543 lib1545 \ lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \ - lib1558 lib1559 lib1560 lib1564 lib1565 lib1567 lib1568 lib1569 \ + lib1558 lib1559 lib1560 lib1564 lib1565 lib1567 lib1568 lib1569 lib1571 \ + lib1576 lib1578 \ lib1591 lib1592 lib1593 lib1594 lib1596 lib1597 lib1598 \ \ lib1662 \ @@ -523,6 +524,14 @@ lib1568_SOURCES = lib1568.c $(SUPPORTFILES) lib1569_SOURCES = lib1569.c $(SUPPORTFILES) +lib1571_SOURCES = lib1571.c $(SUPPORTFILES) +lib1571_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1571 + +lib1576_SOURCES = lib1576.c $(SUPPORTFILES) + +lib1578_SOURCES = lib1576.c $(SUPPORTFILES) +lib1578_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1578 + lib1591_SOURCES = lib1591.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1591_LDADD = $(TESTUTIL_LIBS) diff --git a/tests/libtest/lib1571.c b/tests/libtest/lib1571.c new file mode 100644 index 0000000000..3a12642efe --- /dev/null +++ b/tests/libtest/lib1571.c @@ -0,0 +1,72 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "test.h" + +#include "memdebug.h" + +CURLcode test(char *URL) +{ + CURLcode res; + CURL *curl; + int testno = atoi(libtest_arg2); + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + if(!curl) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + test_setopt(curl, CURLOPT_HEADER, 1L); + test_setopt(curl, CURLOPT_VERBOSE, 1L); + test_setopt(curl, CURLOPT_URL, URL); + if((testno == 1571) || (testno == 1575) || (testno == 1581)) { + test_setopt(curl, CURLOPT_POSTFIELDS, "moo"); + } + if(testno == 1581) { + test_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + } + + test_setopt(curl, CURLOPT_CUSTOMREQUEST, "IGLOO"); + if((testno == 1574) || (testno == 1575)) { + test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); + } + else { + test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); + } + + res = curl_easy_perform(curl); + +test_cleanup: + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return res; +} diff --git a/tests/libtest/lib1576.c b/tests/libtest/lib1576.c new file mode 100644 index 0000000000..46952e3eea --- /dev/null +++ b/tests/libtest/lib1576.c @@ -0,0 +1,86 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "test.h" + +#include "memdebug.h" + +static char testdata[] = "request indicates that the client, which made"; + +static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t amount = nmemb * size; /* Total bytes curl wants */ + if(amount < strlen(testdata)) { + return strlen(testdata); + } + (void)stream; + memcpy(ptr, testdata, strlen(testdata)); + return strlen(testdata); +} + +CURLcode test(char *URL) +{ + CURLcode res; + CURL *curl; + struct curl_slist *pHeaderList = NULL; + int testno = atoi(libtest_arg2); + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + curl = curl_easy_init(); + if(!curl) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + test_setopt(curl, CURLOPT_HEADER, 1L); + test_setopt(curl, CURLOPT_VERBOSE, 1L); + test_setopt(curl, CURLOPT_URL, URL); + test_setopt(curl, CURLOPT_UPLOAD, 1L); + test_setopt(curl, CURLOPT_READFUNCTION, read_callback); + test_setopt(curl, CURLOPT_INFILESIZE, (long)strlen(testdata)); + + test_setopt(curl, CURLOPT_CUSTOMREQUEST, "CURL"); + if(testno == 1578) { + test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); + } + else { + test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); + } + /* Remove "Expect: 100-continue" */ + pHeaderList = curl_slist_append(pHeaderList, "Expect:"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaderList); + res = curl_easy_perform(curl); + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + curl_slist_free_all(pHeaderList); + + return res; +}