cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Patch for 301/302 redirect after post

From: Martin Drasar <drasar_at_optimsys.cz>
Date: Tue, 12 Aug 2008 14:43:34 +0200

Daniel Stenberg napsal(a):
> On Tue, 12 Aug 2008, Martin Drasar wrote:
>
>> Here's the complete patch with src/main.c fixed.
>
> If you're going for gold points, you could look into adding a test case
> as well that verifies this feature.
>
> BTW, the patch isn't correct:
>
> + data->set.post301 = (CURL_REDIR_GET_ALL != va_arg(param, long)) &&
> + (CURL_REDIR_POST_302 != va_arg(param, long));
> + data->set.post302 = (CURL_REDIR_GET_ALL != va_arg(param, long)) &&
> + (CURL_REDIR_POST_301 != va_arg(param, long));
>
> va_arg() pops an argument from the stack, so you cannot call it multiple
> times like this and rely on it. It needs to be done once to pull the
> single argument.
>

D'oh, that was lame!

Fixed, with test case added (it's just modified post301 test case 1012).

-- 
   Martin Drasar, Developer / Analyst
   OptimSys, s.r.o.
   drasar_at_optimsys.cz
   Tel: +420 541 143 065
   Fax: +420 541 143 066
   http://www.optimsys.cz

? patch_redirect
Index: include/curl/curl.h
===================================================================
RCS file: /cvsroot/curl/curl/include/curl/curl.h,v
retrieving revision 1.361
diff -u -r1.361 curl.h
--- include/curl/curl.h 7 Aug 2008 00:29:08 -0000 1.361
+++ include/curl/curl.h 12 Aug 2008 12:36:44 -0000
@@ -1120,8 +1120,9 @@
   CINIT(NEW_FILE_PERMS, LONG, 159),
   CINIT(NEW_DIRECTORY_PERMS, LONG, 160),
 
- /* Obey RFC 2616/10.3.2 and keep POSTs as POSTs after a 301 */
- CINIT(POST301, LONG, 161),
+ /* Set the behaviour of POST when redirecting. Values must be set to one
+ of CURL_REDIR* enums below */
+ CINIT(POSTREDIR, LONG, 161),
 
   /* used by scp/sftp to verify the host's public key */
   CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162),
@@ -1159,6 +1160,11 @@
                           the obsolete stuff removed! */
 
 /* Backwards compatibility with older names */
+/* These are scheduled to disappear by 2011 */
+
+/* This one was added in version 7.18.2 */
+#define CURLOPT_POST301 CURLOPT_POSTREDIR
+
 /* These are scheduled to disappear by 2009 */
 
 /* The following were added in 7.17.0 */
@@ -1223,6 +1229,16 @@
   CURL_SSLVERSION_LAST /* never use, keep last */
 };
 
+/* enums to use with CURLOPT_POSTREDIR.
+ CURL_REDIR_POST_301 and CURL_REDIR_POST_302 can be bitwise ORed so that
+ CURL_REDIR_POST_301 | CURL_REDIR_POST_302 == CURL_REDIR_POST_ALL */
+enum {
+ CURL_REDIR_GET_ALL,
+ CURL_REDIR_POST_301,
+ CURL_REDIR_POST_302,
+ CURL_REDIR_POST_ALL
+};
+
 
 typedef enum {
   CURL_TIMECOND_NONE,
Index: lib/transfer.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/transfer.c,v
retrieving revision 1.399
diff -u -r1.399 transfer.c
--- lib/transfer.c 4 Aug 2008 22:00:22 -0000 1.399
+++ lib/transfer.c 12 Aug 2008 12:36:44 -0000
@@ -2192,7 +2192,8 @@
      * libcurl gets the page that most user agents would get, libcurl has to
      * force GET.
      *
- * This behaviour can be overriden with CURLOPT_POST301.
+ * This behaviour can be overriden with CURLOPT_POST301 or
+ * CURLOPT_POSTREDIR
      */
     if( (data->set.httpreq == HTTPREQ_POST
          || data->set.httpreq == HTTPREQ_POST_FORM)
@@ -2219,7 +2220,18 @@
     status. When interoperability with such clients is a concern, the
     302 status code may be used instead, since most user agents react
     to a 302 response as described here for 303.
+
+ This behaviour can be overriden with CURLOPT_POSTREDIR
     */
+ if( (data->set.httpreq == HTTPREQ_POST
+ || data->set.httpreq == HTTPREQ_POST_FORM)
+ && !data->set.post302) {
+ infof(data,
+ "Violate RFC 2616/10.3.3 and switch from POST to GET\n");
+ data->set.httpreq = HTTPREQ_GET;
+ }
+ break;
+
   case 303: /* See Other */
     /* Disable both types of POSTs, since doing a second POST when
      * following isn't what anyone would want! */
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.726
diff -u -r1.726 url.c
--- lib/url.c 1 Aug 2008 02:09:08 -0000 1.726
+++ lib/url.c 12 Aug 2008 12:36:45 -0000
@@ -1021,12 +1021,23 @@
     data->set.maxredirs = va_arg(param, long);
     break;
 
- case CURLOPT_POST301:
+ case CURLOPT_POSTREDIR:
+ {
     /*
- * Obey RFC 2616/10.3.2 and resubmit a POST as a POST after a 301.
- */
- data->set.post301 = (bool)(0 != va_arg(param, long));
- break;
+ * Set the behaviour of POST when redirecting
+ * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302
+ * CURL_REDIR_POST_301 - POST is kept as POST after 301
+ * CURL_REDIR_POST_302 - POST is kept as POST after 302
+ * CURL_REDIR_POST_ALL - POST is kept as POST after 301 and 302
+ * other - POST is kept as POST after 301 and 302
+ */
+ long postRedir = va_arg(param, long);
+ data->set.post301 = (CURL_REDIR_GET_ALL != postRedir) &&
+ (CURL_REDIR_POST_302 != postRedir);
+ data->set.post302 = (CURL_REDIR_GET_ALL != postRedir) &&
+ (CURL_REDIR_POST_301 != postRedir);
+ }
+ break;
 
   case CURLOPT_POST:
     /* Does this option serve a purpose anymore? Yes it does, when
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.384
diff -u -r1.384 urldata.h
--- lib/urldata.h 30 Jul 2008 21:55:27 -0000 1.384
+++ lib/urldata.h 12 Aug 2008 12:36:45 -0000
@@ -1348,6 +1348,7 @@
                         for infinity */
   bool post301; /* Obey RFC 2616/10.3.2 and keep POSTs as POSTs after a
                         301 */
+ bool post302; /* keep POSTs as POSTs after a 302 */
   bool free_referer; /* set TRUE if 'referer' points to a string we
                         allocated */
   void *postfields; /* if POST, set the fields' values here */
Index: src/main.c
===================================================================
RCS file: /cvsroot/curl/curl/src/main.c,v
retrieving revision 1.468
diff -u -r1.468 main.c
--- src/main.c 1 Aug 2008 18:41:14 -0000 1.468
+++ src/main.c 12 Aug 2008 12:36:45 -0000
@@ -508,6 +508,7 @@
   char *libcurl; /* output libcurl code to this file name */
   bool raw;
   bool post301;
+ bool post302;
   bool nokeepalive; /* for keepalive needs */
   long alivetime;
 
@@ -732,6 +733,7 @@
     " -o/--output <file> Write output to <file> instead of stdout",
     " --pass <pass> Pass phrase for the private key (SSL/SSH)",
     " --post301 Do not switch to GET after following a 301 redirect (H)",
+ " --post302 Do not switch to GET after following a 302 redirect (H)",
     " -#/--progress-bar Display transfer progress as a progress bar",
     " -x/--proxy <host[:port]> Use HTTP proxy on given port",
     " --proxy-anyauth Pick \"any\" proxy authentication method (H)",
@@ -1639,6 +1641,7 @@
     {"$1", "keepalive", FALSE}, /* listed as --no-keepalive in the help */
     {"$2", "socks5-hostname", TRUE},
     {"$3", "keepalive-time", TRUE},
+ {"$4", "post302", FALSE},
 
     {"0", "http1.0", FALSE},
     {"1", "tlsv1", FALSE},
@@ -2147,6 +2150,9 @@
         if(str2num(&config->alivetime, nextarg))
           return PARAM_BAD_NUMERIC;
         break;
+ case '4': /* --post302 */
+ config->post302 = toggle;
+ break;
       }
       break;
     case '#': /* --progress-bar */
@@ -4757,12 +4763,15 @@
         }
 
         /* curl 7.17.1 */
- my_setopt(curl, CURLOPT_POST301, config->post301);
         if (!config->nokeepalive) {
           my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockoptcallback);
           my_setopt(curl, CURLOPT_SOCKOPTDATA, config);
         }
 
+ /* curl 7.18.2 */
+ my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
+ (config->post302 ? CURL_REDIR_POST_302 : FALSE));
+
         retry_numretries = config->req_retry;
 
         retrystart = cutil_tvnow();

<testcase>
<info>
<keywords>
HTTP
HTTP POST
followlocation
</keywords>
</info>
#
# Server-side
<reply>
<data>
HTTP/1.1 302 OK swsclose
Location: moo.html&testcase=/10590002
Date: Thu, 09 Nov 2010 14:49:00 GMT
Connection: close

</data>
<data2>
HTTP/1.1 200 OK swsclose
Location: this should be ignored
Date: Thu, 09 Nov 2010 14:49:00 GMT
Connection: close

body
</data2>
<datacheck>
HTTP/1.1 302 OK swsclose
Location: moo.html&testcase=/10590002
Date: Thu, 09 Nov 2010 14:49:00 GMT
Connection: close

HTTP/1.1 200 OK swsclose
Location: this should be ignored
Date: Thu, 09 Nov 2010 14:49:00 GMT
Connection: close

body
</datacheck>
</reply>

#
# Client-side
<client>
<server>
http
</server>
 <name>
HTTP POST with 302 redirect and --post302
 </name>
 <command>
http://%HOSTIP:%HTTPPORT/blah/1059 -L -d "moo" --post302
</command>
</client>

#
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol nonewline="yes">
POST /blah/1059 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Content-Length: 3
Content-Type: application/x-www-form-urlencoded

mooPOST /blah/moo.html&testcase=/10590002 HTTP/1.1
User-Agent: curl/7.10 (i686-pc-linux-gnu) libcurl/7.10 OpenSSL/0.9.6c ipv6 zlib/1.1.3
Host: %HOSTIP:%HTTPPORT
Accept: */*
Content-Length: 3
Content-Type: application/x-www-form-urlencoded

moo
</protocol>
</verify>
</testcase>
Received on 2008-08-12