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
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
~~~c
#include <curl/curl.h>
-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
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,
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
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
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
#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 */
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)
{
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
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)
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 */
((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".
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";
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)
/*
* 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:
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 */
};
/*
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 */
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 */
\
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 \
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+ <name>
+CURLFOLLOW_OBEYCODE with custom POST method, 302 => GET
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1571
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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: */*
+
+
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1571
+</tool>
+
+ <name>
+CURLFOLLOW_OBEYCODE with custom POST method, 308 => custom
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1571
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1571
+</tool>
+
+ <name>
+CURLFOLLOW_OBEYCODE with custom GET method, 301 => custom
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1573
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol crlf="yes">
+IGLOO /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+IGLOO /%TESTNUMBER0001 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_FIRSTONLY
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1571
+</tool>
+
+<name>
+CURLFOLLOW_FIRSTONLY with custom GET method, 301 => GET
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1574
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol crlf="yes">
+IGLOO /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+GET /%TESTNUMBER0001 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_FIRSTONLY
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1571
+</tool>
+
+<name>
+CURLFOLLOW_FIRSTONLY with custom POST method, 308 => POST
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1575
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<name>
+CURLFOLLOW_OBEYCODE with custom PUT method, 302 => custom
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1576
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1576
+</tool>
+
+<name>
+CURLFOLLOW_OBEYCODE with custom PUT method, 308 => custom
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER %TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_FIRSTONLY
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1576
+</tool>
+
+<name>
+CURLFOLLOW_FIRSTONLY with custom PUT method, 302 => PUT
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER %TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1576
+</tool>
+
+<name>
+CURLFOLLOW_OBEYCODE with custom PUT method, 303 => GET
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER %TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol crlf="yes">
+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: */*
+
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_FIRSTONLY
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1576
+</tool>
+
+<name>
+CURLFOLLOW_FIRSTONLY with custom PUT method, 308 => PUT
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1578
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+CURLFOLLOW_OBEYCODE
+CURLOPT_FOLLOWLOCATION
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+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-
+</data>
+<data1 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+
+-bar-
+</data>
+<datacheck crlf="yes">
+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-
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<tool>
+lib1571
+</tool>
+
+ <name>
+CURLFOLLOW_OBEYCODE with custom POST301 method, 301 => custom
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER 1581
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes" crlf="yes">
+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
+</protocol>
+</verify>
+</testcase>
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 \
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)
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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;
+}
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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;
+}