]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: add CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY
authorDaniel Stenberg <daniel@haxx.se>
Mon, 3 Mar 2025 10:35:48 +0000 (11:35 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 3 Mar 2025 10:35:48 +0000 (11:35 +0100)
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

23 files changed:
docs/KNOWN_BUGS
docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md
docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md
docs/libcurl/symbols-in-versions
include/curl/curl.h
lib/http.c
lib/setopt.c
lib/urldata.h
tests/data/Makefile.am
tests/data/test1571 [new file with mode: 0644]
tests/data/test1572 [new file with mode: 0644]
tests/data/test1573 [new file with mode: 0644]
tests/data/test1574 [new file with mode: 0644]
tests/data/test1575 [new file with mode: 0644]
tests/data/test1576 [new file with mode: 0644]
tests/data/test1577 [new file with mode: 0644]
tests/data/test1578 [new file with mode: 0644]
tests/data/test1579 [new file with mode: 0644]
tests/data/test1580 [new file with mode: 0644]
tests/data/test1581 [new file with mode: 0644]
tests/libtest/Makefile.inc
tests/libtest/lib1571.c [new file with mode: 0644]
tests/libtest/lib1576.c [new file with mode: 0644]

index 0ae0af1c4e33c62a4e576e189965f576b8c1426e..98d6ff4597d455571354ece7e0e92e9ac6dc5728 100644 (file)
@@ -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
 
index 9276a824ce52644672cb8cd1179115509040d8c1..060cfd9c777a6e4e748436ccfb5fbc60187c909b 100644 (file)
@@ -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
 
index 026fb35528a7f5aa281275bf765805dc3945e998..af54e17e13e21a40fdbc9ab48762dfa82a3a9dac 100644 (file)
@@ -25,19 +25,23 @@ CURLOPT_FOLLOWLOCATION - follow HTTP 3xx redirects
 ~~~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
@@ -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
index 0b566edbc3b19d5614e449d3c288d7039596f794..39771cafc9c45cb77572e991f7cda17c570ade2c 100644 (file)
@@ -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
index 346872b39d2ee19bd3e156120418c35a2b4dd892..0d86ddac9b0befda357faa2ba33be9102662c6d7 100644 (file)
@@ -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 */
index 944c35d63c17ce1e00ebc0683ab3939270035722..af05cf6bd0c62de0a23f2b36b945c2b8a56ac0b4 100644 (file)
@@ -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)
index ef05cfb6ef3a98bfd67e4912e90b146c200104ca..e3f944bdd3028ee350051f530bc8aabf3b6ac679 100644 (file)
@@ -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:
index e04200d2e7429fad355051cccc4847a76b0b3d43..e944a95c07c025c8498b898f972c38f5b2c9638b 100644 (file)
@@ -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 */
index 015d568c537eb48ae9d838dc83f698bb84ce47ce..0f4e2c13869201687cf68341b4ffa4f5fa3f66f4 100644 (file)
@@ -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 (file)
index 0000000..957dbde
--- /dev/null
@@ -0,0 +1,95 @@
+<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>
diff --git a/tests/data/test1572 b/tests/data/test1572
new file mode 100644 (file)
index 0000000..cb06637
--- /dev/null
@@ -0,0 +1,97 @@
+<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>
diff --git a/tests/data/test1573 b/tests/data/test1573
new file mode 100644 (file)
index 0000000..f0d8889
--- /dev/null
@@ -0,0 +1,92 @@
+<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>
diff --git a/tests/data/test1574 b/tests/data/test1574
new file mode 100644 (file)
index 0000000..30b1a92
--- /dev/null
@@ -0,0 +1,92 @@
+<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>
diff --git a/tests/data/test1575 b/tests/data/test1575
new file mode 100644 (file)
index 0000000..300856e
--- /dev/null
@@ -0,0 +1,97 @@
+<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>
diff --git a/tests/data/test1576 b/tests/data/test1576
new file mode 100644 (file)
index 0000000..e2a3472
--- /dev/null
@@ -0,0 +1,95 @@
+<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>
diff --git a/tests/data/test1577 b/tests/data/test1577
new file mode 100644 (file)
index 0000000..13a0bc3
--- /dev/null
@@ -0,0 +1,95 @@
+<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>
diff --git a/tests/data/test1578 b/tests/data/test1578
new file mode 100644 (file)
index 0000000..1518b3c
--- /dev/null
@@ -0,0 +1,95 @@
+<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>
diff --git a/tests/data/test1579 b/tests/data/test1579
new file mode 100644 (file)
index 0000000..3214e19
--- /dev/null
@@ -0,0 +1,93 @@
+<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>
diff --git a/tests/data/test1580 b/tests/data/test1580
new file mode 100644 (file)
index 0000000..08295cd
--- /dev/null
@@ -0,0 +1,95 @@
+<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>
diff --git a/tests/data/test1581 b/tests/data/test1581
new file mode 100644 (file)
index 0000000..2979e5f
--- /dev/null
@@ -0,0 +1,97 @@
+<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>
index cf4d6e7122a9b5b63fd9625b2c849d2f1b158396..12cdeb6aea2e33d1753ed578181920fd9de47a46 100644 (file)
@@ -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 (file)
index 0000000..3a12642
--- /dev/null
@@ -0,0 +1,72 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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;
+}
diff --git a/tests/libtest/lib1576.c b/tests/libtest/lib1576.c
new file mode 100644 (file)
index 0000000..46952e3
--- /dev/null
@@ -0,0 +1,86 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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;
+}