From 18110b519c56a464bca4258332279c580f07a52f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 5 Sep 2008 16:13:20 +0000 Subject: [PATCH] - Martin Drasar provided the CURLOPT_POSTREDIR patch. It renames CURLOPT_POST301 (but adds a define for backwards compatibility for you who don't define CURL_NO_OLDIES). This option allows you to now also change the libcurl behavior for a HTTP response 302 after a POST to not use GET in the subsequent request (when CURLOPT_FOLLOWLOCATION is enabled). I edited the patch somewhat before commit. The curl tool got a matching --post302 option. Test case 1076 was added to verify this. --- CHANGES | 8 +++++ RELEASE-NOTES | 5 +-- docs/curl.1 | 7 ++++ include/curl/curl.h | 18 ++++++++-- lib/transfer.c | 13 ++++++- lib/url.c | 21 +++++++---- lib/urldata.h | 1 + src/main.c | 29 ++++++++++------ tests/data/Makefile.am | 2 +- tests/data/test1076 | 79 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 161 insertions(+), 22 deletions(-) create mode 100644 tests/data/test1076 diff --git a/CHANGES b/CHANGES index 1908387ce..66ca25c50 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,14 @@ Changelog Daniel Stenberg (5 Sep 2008) +- Martin Drasar provided the CURLOPT_POSTREDIR patch. It renames + CURLOPT_POST301 (but adds a define for backwards compatibility for you who + don't define CURL_NO_OLDIES). This option allows you to now also change the + libcurl behavior for a HTTP response 302 after a POST to not use GET in the + subsequent request (when CURLOPT_FOLLOWLOCATION is enabled). I edited the + patch somewhat before commit. The curl tool got a matching --post302 + option. Test case 1076 was added to verify this. + - Introducing CURLOPT_CERTINFO and the corresponding CURLINFO_CERTINFO. By enabling this feature with CURLOPT_CERTINFO for a request using SSL (HTTPS or FTPS), libcurl will gather lots of server certificate info and that info diff --git a/RELEASE-NOTES b/RELEASE-NOTES index c1f3bfcca..8b3d204da 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,7 +1,7 @@ Curl and libcurl 7.19.1 Public curl releases: 107 - Command line options: 127 + Command line options: 128 curl_easy_setopt() options: 154 Public functions in libcurl: 58 Known libcurl bindings: 36 @@ -11,6 +11,7 @@ This release includes the following changes: o pkg-config can now show supported_protocols and supported_features o Added CURLOPT_CERTINFO and CURLINFO_CERTINFO + o Added CURLOPT_POSTREDIR This release includes the following bugfixes: @@ -30,6 +31,6 @@ This release would not have looked like this without help, code, reports and advice from friends like these: Keith Mok, Yang Tse, Daniel Fandrich, Guenter Knauf, Dmitriy Sergeyev, - Linus Nielsen Feltzing + Linus Nielsen Feltzing, Martin Drasar Thanks! (and sorry if I forgot to mention someone) diff --git a/docs/curl.1 b/docs/curl.1 index 57cb79dde..0ce5c9fbf 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -870,6 +870,13 @@ in web browsers, so curl does the conversion by default to maintain consistency. However, a server may requires a POST to remain a POST after such a redirection. This option is meaningful only when using \fI-L/--location\fP (Added in 7.17.1) +.IP "--post302" +Tells curl to respect RFC 2616/10.3.2 and not convert POST requests into GET +requests when following a 302 redirection. The non-RFC behaviour is ubiquitous +in web browsers, so curl does the conversion by default to maintain +consistency. However, a server may requires a POST to remain a POST after such +a redirection. This option is meaningful only when using \fI-L/--location\fP +(Added in 7.19.1) .IP "--proxy-anyauth" Tells curl to pick a suitable authentication method when communicating with the given proxy. This might cause an extra request/response round-trip. (Added diff --git a/include/curl/curl.h b/include/curl/curl.h index 2b574c9d7..d93fd6764 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1103,8 +1103,9 @@ typedef enum { 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* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), /* used by scp/sftp to verify the host's public key */ CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), @@ -1147,6 +1148,11 @@ typedef enum { the obsolete stuff removed! */ /* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + /* These are scheduled to disappear by 2009 */ /* The following were added in 7.17.0 */ @@ -1211,6 +1217,14 @@ enum { CURL_SSLVERSION_LAST /* never use, keep last */ }; +/* symbols 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 */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_ALL (CURL_REDIR_POST_301|CURL_REDIR_POST_302) typedef enum { CURL_TIMECOND_NONE, diff --git a/lib/transfer.c b/lib/transfer.c index 2fd6d4cf3..ddacb442a 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -2286,7 +2286,7 @@ CURLcode Curl_follow(struct SessionHandle *data, * libcurl gets the page that most user agents would get, libcurl has to * force GET. * - * This behaviour can be overridden with CURLOPT_POST301. + * This behaviour can be overridden with CURLOPT_POSTREDIR. */ if( (data->set.httpreq == HTTPREQ_POST || data->set.httpreq == HTTPREQ_POST_FORM) @@ -2313,7 +2313,18 @@ CURLcode Curl_follow(struct SessionHandle *data, 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! */ diff --git a/lib/url.c b/lib/url.c index d6dec0d0a..a51ba4394 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1028,12 +1028,21 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, 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. + * 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 */ - data->set.post301 = (bool)(0 != va_arg(param, long)); - break; + long postRedir = va_arg(param, long); + data->set.post301 = (postRedir & CURL_REDIR_POST_301)?1:0; + data->set.post302 = (postRedir & CURL_REDIR_POST_302)?1:0; + } + break; case CURLOPT_POST: /* Does this option serve a purpose anymore? Yes it does, when @@ -2200,13 +2209,13 @@ CURLcode Curl_disconnect(struct connectdata *conn) if (has_host_ntlm) { data->state.authhost.done = FALSE; data->state.authhost.picked = - data->state.authhost.want; + data->state.authhost.want; } if (has_proxy_ntlm) { data->state.authproxy.done = FALSE; data->state.authproxy.picked = - data->state.authproxy.want; + data->state.authproxy.want; } if (has_host_ntlm || has_proxy_ntlm) { diff --git a/lib/urldata.h b/lib/urldata.h index f1a001aa0..a8a7555aa 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1358,6 +1358,7 @@ struct UserDefined { 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 */ diff --git a/src/main.c b/src/main.c index e9f9d30a2..81f1dfd9c 100644 --- a/src/main.c +++ b/src/main.c @@ -557,6 +557,7 @@ struct Configurable { char *libcurl; /* output libcurl code to this file name */ bool raw; bool post301; + bool post302; bool nokeepalive; /* for keepalive needs */ long alivetime; @@ -780,6 +781,7 @@ static void help(void) " -o/--output Write output to instead of stdout", " --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 Use HTTP proxy on given port", " --proxy-anyauth Pick \"any\" proxy authentication method (H)", @@ -1669,6 +1671,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$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}, @@ -2177,6 +2180,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ if(str2num(&config->alivetime, nextarg)) return PARAM_BAD_NUMERIC; break; + case '4': /* --post302 */ + config->post302 = toggle; + break; } break; case '#': /* --progress-bar */ @@ -2946,19 +2952,19 @@ static const char *unslashquote(const char *line, char *param) /* default is to output the letter after the backslash */ switch(out = *line) { case '\0': - continue; /* this'll break out of the loop */ + continue; /* this'll break out of the loop */ case 't': - out='\t'; - break; + out='\t'; + break; case 'n': - out='\n'; - break; + out='\n'; + break; case 'r': - out='\r'; - break; + out='\r'; + break; case 'v': - out='\v'; - break; + out='\v'; + break; } *param++=out; line++; @@ -4777,12 +4783,15 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) } /* 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.19.1 (the 301 version existed in 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(); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 185d385e9..a33e3d2e1 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -57,7 +57,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ test1048 test1049 test1050 test1051 test1052 test1053 test1054 test1055 \ test1056 test1057 test1058 test1059 test1060 test1061 test1062 test1063 \ test1064 test1065 test1066 test1067 test1068 test1069 test1070 test1071 \ - test1072 test1073 test1074 test1075 + test1072 test1073 test1074 test1075 test1076 filecheck: @mkdir test-place; \ diff --git a/tests/data/test1076 b/tests/data/test1076 new file mode 100644 index 000000000..ad079eb94 --- /dev/null +++ b/tests/data/test1076 @@ -0,0 +1,79 @@ + + + +HTTP +HTTP POST +followlocation + + +# +# Server-side + + +HTTP/1.1 302 OK swsclose +Location: moo.html&testcase=/10760002 +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 + + +HTTP/1.1 302 OK swsclose +Location: moo.html&testcase=/10760002 +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 + + + +# +# Client-side + + +http + + +HTTP POST with 302 redirect and --post302 + + +http://%HOSTIP:%HTTPPORT/blah/1076 -L -d "moo" --post302 + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +POST /blah/1076 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooPOST /blah/moo.html&testcase=/10760002 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 + + +