From 9c845be2797e2047547ec247cb037471aeb48bb0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 1 Jun 2020 22:58:46 +0200 Subject: urldata: let the HTTP method be in the set.* struct When the method is updated inside libcurl we must still not change the method as set by the user as then repeated transfers with that same handle might not execute the same operation anymore! This fixes the libcurl part of #5462 Test 1633 added to verify. Closes #5499 --- lib/http.c | 24 ++++++------ lib/http2.c | 2 +- lib/mqtt.c | 2 +- lib/rtsp.c | 6 +-- lib/setopt.c | 23 +++++++----- lib/transfer.c | 39 ++++++++++---------- lib/url.c | 12 +----- lib/urldata.h | 3 +- lib/vquic/ngtcp2.c | 2 +- lib/vquic/quiche.c | 2 +- tests/data/Makefile.inc | 2 +- tests/data/test1633 | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 tests/data/test1633 diff --git a/lib/http.c b/lib/http.c index 0c3df4c50..a0d3eca24 100644 --- a/lib/http.c +++ b/lib/http.c @@ -431,7 +431,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) skip this rewinding stuff */ return CURLE_OK; - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_GET: case HTTPREQ_HEAD: return CURLE_OK; @@ -452,7 +452,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) } else { /* figure out how much data we are expected to send */ - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_PUT: if(data->state.infilesize != -1) @@ -594,8 +594,8 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) #endif if(pickhost || pickproxy) { - if((data->set.httpreq != HTTPREQ_GET) && - (data->set.httpreq != HTTPREQ_HEAD) && + if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD) && !conn->bits.rewindaftersend) { result = http_perhapsrewind(conn); if(result) @@ -616,8 +616,8 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) authentication is not "done" yet and no authentication seems to be required and we didn't try HEAD or GET */ - if((data->set.httpreq != HTTPREQ_GET) && - (data->set.httpreq != HTTPREQ_HEAD)) { + if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD)) { data->req.newurl = strdup(data->change.url); /* clone URL */ if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; @@ -1774,11 +1774,11 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, header as that will produce *two* in the same request! */ checkprefix("Host:", compare)) ; - else if(data->set.httpreq == HTTPREQ_POST_FORM && + else if(data->state.httpreq == HTTPREQ_POST_FORM && /* this header (extended by formdata.c) is sent later */ checkprefix("Content-Type:", compare)) ; - else if(data->set.httpreq == HTTPREQ_POST_MIME && + else if(data->state.httpreq == HTTPREQ_POST_MIME && /* this header is sent later */ checkprefix("Content-Type:", compare)) ; @@ -1915,7 +1915,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) const char *te = ""; /* transfer-encoding */ const char *ptr; const char *request; - Curl_HttpReq httpreq = data->set.httpreq; + Curl_HttpReq httpreq = data->state.httpreq; #if !defined(CURL_DISABLE_COOKIES) char *addcookies = NULL; #endif @@ -3320,7 +3320,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if((k->size == -1) && !k->chunk && !conn->bits.close && (conn->httpversion == 11) && !(conn->handler->protocol & CURLPROTO_RTSP) && - data->set.httpreq != HTTPREQ_HEAD) { + data->state.httpreq != HTTPREQ_HEAD) { /* On HTTP 1.1, when connection is not to get closed, but no Content-Length nor Transfer-Encoding chunked have been received, according to RFC2616 section 4.4 point 5, we @@ -3415,7 +3415,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * continue sending even if it gets discarded */ - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_PUT: case HTTPREQ_POST: case HTTPREQ_POST_FORM: @@ -3671,7 +3671,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * depending on how authentication is working. Other codes * are definitely errors, so give up here. */ - if(data->state.resume_from && data->set.httpreq == HTTPREQ_GET && + if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && k->httpcode == 416) { /* "Requested Range Not Satisfiable", just proceed and pretend this is no error */ diff --git a/lib/http2.c b/lib/http2.c index 7d56e2280..61990019e 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -2031,7 +2031,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, h2_pri_spec(conn->data, &pri_spec); - switch(conn->data->set.httpreq) { + switch(conn->data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: case HTTPREQ_POST_MIME: diff --git a/lib/mqtt.c b/lib/mqtt.c index 43a3b6e53..d09aab4ee 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -591,7 +591,7 @@ static CURLcode mqtt_doing(struct connectdata *conn, bool *done) if(result) break; - if(conn->data->set.httpreq == HTTPREQ_POST) { + if(conn->data->state.httpreq == HTTPREQ_POST) { result = mqtt_publish(conn); if(!result) { result = mqtt_disconnect(conn); diff --git a/lib/rtsp.c b/lib/rtsp.c index 9c86b6171..2a9c683c3 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -498,14 +498,14 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(data->set.upload) { putsize = data->state.infilesize; - data->set.httpreq = HTTPREQ_PUT; + data->state.httpreq = HTTPREQ_PUT; } else { postsize = (data->state.infilesize != -1)? data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); - data->set.httpreq = HTTPREQ_POST; + data->state.httpreq = HTTPREQ_POST; } if(putsize > 0 || postsize > 0) { @@ -543,7 +543,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } else if(rtspreq == RTSPREQ_GET_PARAMETER) { /* Check for an empty GET_PARAMETER (heartbeat) request */ - data->set.httpreq = HTTPREQ_HEAD; + data->state.httpreq = HTTPREQ_HEAD; data->set.opt_no_body = TRUE; } } diff --git a/lib/setopt.c b/lib/setopt.c index fc914aa09..90edf6aa7 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -271,6 +271,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Do not include the body part in the output data stream. */ data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; + if(data->set.opt_no_body) + /* in HTTP lingo, no body means using the HEAD request... */ + data->set.method = HTTPREQ_HEAD; break; case CURLOPT_FAILONERROR: /* @@ -292,13 +295,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE; if(data->set.upload) { /* If this is HTTP, PUT is what's needed to "upload" */ - data->set.httpreq = HTTPREQ_PUT; + data->set.method = HTTPREQ_PUT; data->set.opt_no_body = FALSE; /* this is implied */ } else /* In HTTP, the opposite of upload is GET (unless NOBODY is true as then this can be changed to HEAD later on) */ - data->set.httpreq = HTTPREQ_GET; + data->set.method = HTTPREQ_GET; break; case CURLOPT_REQUEST_TARGET: result = Curl_setstropt(&data->set.str[STRING_TARGET], @@ -516,11 +519,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) CURLOPT_POSTFIELDS isn't used and the POST data is read off the callback! */ if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_POST; + data->set.method = HTTPREQ_POST; data->set.opt_no_body = FALSE; /* this is implied */ } else - data->set.httpreq = HTTPREQ_GET; + data->set.method = HTTPREQ_GET; break; case CURLOPT_COPYPOSTFIELDS: @@ -567,7 +570,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) } data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; - data->set.httpreq = HTTPREQ_POST; + data->set.method = HTTPREQ_POST; break; case CURLOPT_POSTFIELDS: @@ -577,7 +580,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.postfields = va_arg(param, void *); /* Release old copied data. */ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.httpreq = HTTPREQ_POST; + data->set.method = HTTPREQ_POST; break; case CURLOPT_POSTFIELDSIZE: @@ -623,7 +626,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set to make us do HTTP POST */ data->set.httppost = va_arg(param, struct curl_httppost *); - data->set.httpreq = HTTPREQ_POST_FORM; + data->set.method = HTTPREQ_POST_FORM; data->set.opt_no_body = FALSE; /* this is implied */ break; #endif /* CURL_DISABLE_HTTP */ @@ -635,7 +638,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_mime_set_subparts(&data->set.mimepost, va_arg(param, curl_mime *), FALSE); if(!result) { - data->set.httpreq = HTTPREQ_POST_MIME; + data->set.method = HTTPREQ_POST_MIME; data->set.opt_no_body = FALSE; /* this is implied */ } break; @@ -830,7 +833,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set to force us do HTTP GET */ if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_GET; + data->set.method = HTTPREQ_GET; data->set.upload = FALSE; /* switch off upload */ data->set.opt_no_body = FALSE; /* this is implied */ } @@ -940,7 +943,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); /* we don't set - data->set.httpreq = HTTPREQ_CUSTOM; + data->set.method = HTTPREQ_CUSTOM; here, we continue as if we were using the already set type and this just changes the actual request keyword */ break; diff --git a/lib/transfer.c b/lib/transfer.c index 319744985..133a4783c 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -433,8 +433,8 @@ CURLcode Curl_readrewind(struct connectdata *conn) } if(data->set.postfields) ; /* do nothing */ - else if(data->set.httpreq == HTTPREQ_POST_MIME || - data->set.httpreq == HTTPREQ_POST_FORM) { + else if(data->state.httpreq == HTTPREQ_POST_MIME || + data->state.httpreq == HTTPREQ_POST_FORM) { if(Curl_mime_rewind(mimepart)) { failf(data, "Cannot rewind mime/post data"); return CURLE_SEND_FAIL_REWIND; @@ -719,7 +719,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, infof(data, "Ignoring the response-body\n"); } if(data->state.resume_from && !k->content_range && - (data->set.httpreq == HTTPREQ_GET) && + (data->state.httpreq == HTTPREQ_GET) && !k->ignorebody) { if(k->size == data->state.resume_from) { @@ -1449,6 +1449,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } } + data->state.httpreq = data->set.method; data->change.url = data->set.str[STRING_SET_URL]; /* Init the SSL session ID cache here. We do it here since we want to do it @@ -1469,10 +1470,10 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.authproxy.want = data->set.proxyauth; Curl_safefree(data->info.wouldredirect); - if(data->set.httpreq == HTTPREQ_PUT) + if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; - else if((data->set.httpreq != HTTPREQ_GET) && - (data->set.httpreq != HTTPREQ_HEAD)) { + else if((data->state.httpreq != HTTPREQ_GET) && + (data->state.httpreq != HTTPREQ_HEAD)) { data->state.infilesize = data->set.postfieldsize; if(data->set.postfields && (data->state.infilesize == -1)) data->state.infilesize = (curl_off_t)strlen(data->set.postfields); @@ -1683,12 +1684,12 @@ CURLcode Curl_follow(struct Curl_easy *data, * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->set.httpreq == HTTPREQ_POST - || data->set.httpreq == HTTPREQ_POST_FORM - || data->set.httpreq == HTTPREQ_POST_MIME) + 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\n"); - data->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_GET; } break; case 302: /* Found */ @@ -1708,12 +1709,12 @@ CURLcode Curl_follow(struct Curl_easy *data, * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->set.httpreq == HTTPREQ_POST - || data->set.httpreq == HTTPREQ_POST_FORM - || data->set.httpreq == HTTPREQ_POST_MIME) + 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\n"); - data->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_GET; } break; @@ -1723,12 +1724,12 @@ CURLcode Curl_follow(struct Curl_easy *data, * method is POST and the user specified to keep it as POST. * https://github.com/curl/curl/issues/5237#issuecomment-614641049 */ - if(data->set.httpreq != HTTPREQ_GET && - ((data->set.httpreq != HTTPREQ_POST && - data->set.httpreq != HTTPREQ_POST_FORM && - data->set.httpreq != HTTPREQ_POST_MIME) || + if(data->state.httpreq != HTTPREQ_GET && + ((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->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_GET; data->set.upload = false; infof(data, "Switch to %s\n", data->set.opt_no_body?"HEAD":"GET"); diff --git a/lib/url.c b/lib/url.c index adc9767db..524d968c2 100644 --- a/lib/url.c +++ b/lib/url.c @@ -462,7 +462,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->postfieldsize = -1; /* unknown size */ set->maxredirs = -1; /* allow any amount by default */ - set->httpreq = HTTPREQ_GET; /* Default HTTP request */ + set->method = HTTPREQ_GET; /* Default HTTP request */ set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */ #ifndef CURL_DISABLE_FTP set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ @@ -3992,17 +3992,9 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) data->state.done = FALSE; /* *_done() is not called yet */ data->state.expect100header = FALSE; - if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ - data->set.httpreq = HTTPREQ_HEAD; - else if(HTTPREQ_HEAD == data->set.httpreq) - /* ... but if unset there really is no perfect method that is the - "opposite" of HEAD but in reality most people probably think GET - then. The important thing is that we can't let it remain HEAD if the - opt_no_body is set FALSE since then we'll behave wrong when getting - HTTP. */ - data->set.httpreq = HTTPREQ_GET; + data->state.httpreq = HTTPREQ_HEAD; k->start = Curl_now(); /* start time */ k->now = k->start; /* current time is now */ diff --git a/lib/urldata.h b/lib/urldata.h index db6af9d10..d3cc5356e 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1390,6 +1390,7 @@ struct UrlState { int stream_weight; CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; + Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ #ifndef CURL_DISABLE_HTTP size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing @@ -1678,7 +1679,7 @@ struct UserDefined { the hostname and port to connect to */ curl_TimeCond timecondition; /* kind of time/date comparison */ time_t timevalue; /* what time to compare with */ - Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ + Curl_HttpReq method; /* what kind of HTTP request (if any) is this */ long httpversion; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ struct ssl_config_data ssl; /* user defined SSL stuff */ diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c index 40bc0f3f0..eaff638b4 100644 --- a/lib/vquic/ngtcp2.c +++ b/lib/vquic/ngtcp2.c @@ -1558,7 +1558,7 @@ static CURLcode http_request(struct connectdata *conn, const void *mem, } } - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: case HTTPREQ_POST_MIME: diff --git a/lib/vquic/quiche.c b/lib/vquic/quiche.c index 83815e037..f93e95ee1 100644 --- a/lib/vquic/quiche.c +++ b/lib/vquic/quiche.c @@ -734,7 +734,7 @@ static CURLcode http_request(struct connectdata *conn, const void *mem, } } - switch(data->set.httpreq) { + switch(data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: case HTTPREQ_POST_MIME: diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 0633ccb2f..7a048f01a 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -196,7 +196,7 @@ test1608 test1609 test1610 test1611 test1612 \ \ test1620 test1621 \ \ -test1630 test1631 test1632 \ +test1630 test1631 test1632 test1633 \ \ test1650 test1651 test1652 test1653 test1654 test1655 \ \ diff --git a/tests/data/test1633 b/tests/data/test1633 new file mode 100644 index 000000000..5d29ef139 --- /dev/null +++ b/tests/data/test1633 @@ -0,0 +1,97 @@ + + + +HTTP +HTTP GET + + + +# +# Server-side + + +HTTP/1.1 301 OK +Accept-Ranges: bytes +Content-Length: 0 +Connection: close +Location: /16330002 + + + +HTTP/1.1 429 too many requests +Retry-After: 1 +Content-Length: 0 +Connection: close + + + + +HTTP/1.1 301 OK +Accept-Ranges: bytes +Content-Length: 0 +Connection: close +Location: /16330002 + +HTTP/1.1 429 too many requests +Retry-After: 1 +Content-Length: 0 +Connection: close + +HTTP/1.1 301 OK +Accept-Ranges: bytes +Content-Length: 0 +Connection: close +Location: /16330002 + +HTTP/1.1 429 too many requests +Retry-After: 1 +Content-Length: 0 +Connection: close + + + + +# +# Client-side + + +http + + +HTTP GET + + +http://%HOSTIP:%HTTPPORT/1633 -d moo --retry 1 -L + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +POST /1633 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooGET /16330002 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + +POST /1633 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooGET /16330002 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + -- cgit v1.2.3