Skip to content

Commit

Permalink
lib: move mimepost data from ->req.p.http to ->state
Browse files Browse the repository at this point in the history
When the legacy CURLOPT_HTTPPOST option is used, it gets converted into
the modem mimpost struct at first use. This data is (now) kept for the
entire transfer and not only per single HTTP request. This re-enables
rewind in the beginning of the second request instead of in end of the
first, as brought by 1b39731.

The request struct is per-request data only.

Extend test 650 to verify.

Fixes #11680
Reported-by: yushicheng7788 on github
Closes #11682
  • Loading branch information
bagder committed Aug 17, 2023
1 parent 821d108 commit 74b87a8
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 45 deletions.
46 changes: 25 additions & 21 deletions lib/http.c
Expand Up @@ -233,7 +233,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
if(!http)
return CURLE_OUT_OF_MEMORY;

Curl_mime_initpart(&http->form);
data->req.p.http = http;
connkeep(conn, "HTTP default");

Expand Down Expand Up @@ -1577,7 +1576,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
return CURLE_OK;

Curl_dyn_free(&http->send_buffer);
Curl_mime_cleanpart(&http->form);
Curl_dyn_reset(&data->state.headerb);
Curl_hyper_done(data);
Curl_ws_done(data);
Expand Down Expand Up @@ -2375,47 +2373,53 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,

switch(httpreq) {
case HTTPREQ_POST_MIME:
http->sendit = &data->set.mimepost;
data->state.mimepost = &data->set.mimepost;
break;
#ifndef CURL_DISABLE_FORM_API
case HTTPREQ_POST_FORM:
/* Convert the form structure into a mime structure. */
Curl_mime_cleanpart(&http->form);
result = Curl_getformdata(data, &http->form, data->set.httppost,
data->state.fread_func);
if(result)
return result;
http->sendit = &http->form;
/* Convert the form structure into a mime structure, then keep
the conversion */
if(!data->state.formp) {
data->state.formp = calloc(sizeof(curl_mimepart), 1);
if(!data->state.formp)
return CURLE_OUT_OF_MEMORY;
Curl_mime_cleanpart(data->state.formp);
result = Curl_getformdata(data, data->state.formp, data->set.httppost,
data->state.fread_func);
if(result)
return result;
data->state.mimepost = data->state.formp;
}
break;
#endif
default:
http->sendit = NULL;
data->state.mimepost = NULL;
}

#ifndef CURL_DISABLE_MIME
if(http->sendit) {
if(data->state.mimepost) {
const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));

/* Read and seek body only. */
http->sendit->flags |= MIME_BODY_ONLY;
data->state.mimepost->flags |= MIME_BODY_ONLY;

/* Prepare the mime structure headers & set content type. */

if(cthdr)
for(cthdr += 13; *cthdr == ' '; cthdr++)
;
else if(http->sendit->kind == MIMEKIND_MULTIPART)
else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
cthdr = "multipart/form-data";

curl_mime_headers(http->sendit, data->set.headers, 0);
result = Curl_mime_prepare_headers(data, http->sendit, cthdr,
curl_mime_headers(data->state.mimepost, data->set.headers, 0);
result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr,
NULL, MIMESTRATEGY_FORM);
curl_mime_headers(http->sendit, NULL, 0);
curl_mime_headers(data->state.mimepost, NULL, 0);
if(!result)
result = Curl_mime_rewind(http->sendit);
result = Curl_mime_rewind(data->state.mimepost);
if(result)
return result;
http->postsize = Curl_mime_size(http->sendit);
http->postsize = Curl_mime_size(data->state.mimepost);
}
#endif

Expand Down Expand Up @@ -2571,7 +2575,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
{
struct curl_slist *hdr;

for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
if(result)
return result;
Expand Down Expand Up @@ -2606,7 +2610,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,

/* Read from mime structure. */
data->state.fread_func = (curl_read_callback) Curl_mime_read;
data->state.in = (void *) http->sendit;
data->state.in = (void *) data->state.mimepost;
http->sending = HTTPSEND_BODY;

/* this sends the buffer and frees all the buffer resources */
Expand Down
5 changes: 0 additions & 5 deletions lib/http.h
Expand Up @@ -198,13 +198,8 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
* HTTP unique setup
***************************************************************************/
struct HTTP {
curl_mimepart *sendit;
curl_off_t postsize; /* off_t to handle large file sizes */
const char *postdata;

/* For FORM posting */
curl_mimepart form;

struct back {
curl_read_callback fread_func; /* backup storage for fread pointer */
void *fread_in; /* backup storage for fread_in pointer */
Expand Down
18 changes: 10 additions & 8 deletions lib/mime.c
Expand Up @@ -1167,14 +1167,16 @@ static void mime_subparts_unbind(void *ptr)

void Curl_mime_cleanpart(curl_mimepart *part)
{
cleanup_part_content(part);
curl_slist_free_all(part->curlheaders);
if(part->flags & MIME_USERHEADERS_OWNER)
curl_slist_free_all(part->userheaders);
Curl_safefree(part->mimetype);
Curl_safefree(part->name);
Curl_safefree(part->filename);
Curl_mime_initpart(part);
if(part) {
cleanup_part_content(part);
curl_slist_free_all(part->curlheaders);
if(part->flags & MIME_USERHEADERS_OWNER)
curl_slist_free_all(part->userheaders);
Curl_safefree(part->mimetype);
Curl_safefree(part->name);
Curl_safefree(part->filename);
Curl_mime_initpart(part);
}
}

/* Recursively delete a mime handle and its parts. */
Expand Down
13 changes: 6 additions & 7 deletions lib/multi.c
Expand Up @@ -1799,9 +1799,8 @@ static CURLcode protocol_connect(struct Curl_easy *data,
*/
static CURLcode readrewind(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
curl_mimepart *mimepart = &data->set.mimepost;
DEBUGASSERT(conn);
DEBUGASSERT(data->conn);

data->state.rewindbeforesend = FALSE; /* we rewind now */

Expand All @@ -1814,12 +1813,12 @@ static CURLcode readrewind(struct Curl_easy *data)
/* We have sent away data. If not using CURLOPT_POSTFIELDS or
CURLOPT_HTTPPOST, call app to rewind
*/
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
struct HTTP *http = data->req.p.http;

if(http->sendit)
mimepart = http->sendit;
#ifndef CURL_DISABLE_HTTP
if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
if(data->state.mimepost)
mimepart = data->state.mimepost;
}
#endif
if(data->set.postfields ||
(data->state.httpreq == HTTPREQ_GET) ||
(data->state.httpreq == HTTPREQ_HEAD))
Expand Down
8 changes: 7 additions & 1 deletion lib/setopt.c
Expand Up @@ -669,11 +669,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
#ifndef CURL_DISABLE_FORM_API
case CURLOPT_HTTPPOST:
/*
* Set to make us do HTTP POST
* Set to make us do HTTP POST. Legacy API-style.
*/
data->set.httppost = va_arg(param, struct curl_httppost *);
data->set.method = HTTPREQ_POST_FORM;
data->set.opt_no_body = FALSE; /* this is implied */
Curl_mime_cleanpart(data->state.formp);
Curl_safefree(data->state.formp);
break;
#endif

Expand Down Expand Up @@ -985,6 +987,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
if(!result) {
data->set.method = HTTPREQ_POST_MIME;
data->set.opt_no_body = FALSE; /* this is implied */
#ifndef CURL_DISABLE_FORM_API
Curl_mime_cleanpart(data->state.formp);
Curl_safefree(data->state.formp);
#endif
}
break;

Expand Down
5 changes: 5 additions & 0 deletions lib/url.c
Expand Up @@ -457,6 +457,11 @@ CURLcode Curl_close(struct Curl_easy **datap)
}
#endif

Curl_mime_cleanpart(data->state.formp);
#ifndef CURL_DISABLE_HTTP
Curl_safefree(data->state.formp);
#endif

/* destruct wildcard structures if it is needed */
Curl_wildcard_dtor(&data->wildcard);
Curl_freeset(data);
Expand Down
3 changes: 3 additions & 0 deletions lib/urldata.h
Expand Up @@ -1405,6 +1405,9 @@ struct UrlState {
struct curl_slist *resolve; /* set to point to the set.resolve list when
this should be dealt with in pretransfer */
#ifndef CURL_DISABLE_HTTP
curl_mimepart *mimepost;
curl_mimepart *formp; /* storage for old API form-posting, alloced on
demand */
size_t trailers_bytes_sent;
struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
headers */
Expand Down
101 changes: 98 additions & 3 deletions tests/data/test650
Expand Up @@ -10,15 +10,26 @@ FORM
#
# Server-side
<reply>
<data>
HTTP/1.1 200 OK
<data nocheck="yes">
HTTP/1.1 301 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Connection: close
Content-Type: text/html
Location: /%TESTNUMBER0002

hello
</data>
<data2 crlf="yes">
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
Content-Length: 6
Connection: close
Content-Type: text/html

-foo-
</data>
</reply>

# Client-side
Expand All @@ -35,7 +46,7 @@ lib%TESTNUMBER
</tool>

<name>
HTTP formpost using form API
HTTP formpost using form API - with redirect and re-POST
</name>
<stdin>
Some data from stdin
Expand Down Expand Up @@ -134,6 +145,90 @@ Content-Disposition: form-data; name="standardinput"
Content-Type: application/octet-stream


16
Some data from stdin

30

--------------------------------

0

POST /%TESTNUMBER0002 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------
Expect: 100-continue

361
------------------------------
Content-Disposition: form-data; name="fieldname"
Content-Type: text/plain
X-customheader-1: Header 1 data
X-customheader-2: Header 2 data

this is what we post to the silly web server
------------------------------
Content-Disposition: form-data; name="fieldnam"

uhis is what we post to the silly web serve
------------------------------
Content-Disposition: form-data; name="multifile"
Content-Type: multipart/mixed; boundary=----------------------------

------------------------------
Content-Disposition: attachment; filename="test%TESTNUMBER.filedata"
Content-Type: application/octet-stream

This is data from a file.

------------------------------
Content-Disposition: attachment; filename="test%TESTNUMBER.filedata"
Content-Type: text/whatever


%if hyper
A5
%else
a5
%endif
This is data from a file.

------------------------------
Content-Disposition: attachment; filename="test%TESTNUMBER.filedata"
Content-Type: text/whatever


%if hyper
AF
%else
af
%endif
This is data from a file.

--------------------------------

------------------------------
Content-Disposition: form-data; name="filecontents"


%if hyper
10F
%else
10f
%endif
This is data from a file.

------------------------------
Content-Disposition: form-data; name="formlength"

1367
------------------------------
Content-Disposition: form-data; name="standardinput"
Content-Type: application/octet-stream


16
Some data from stdin

Expand Down
3 changes: 3 additions & 0 deletions tests/libtest/lib650.c
Expand Up @@ -189,6 +189,9 @@ int test(char *URL)
/* get verbose debug output please */
test_setopt(curl, CURLOPT_VERBOSE, 1L);

test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
test_setopt(curl, CURLOPT_POSTREDIR, (long)CURL_REDIR_POST_301);

/* include headers in the output */
test_setopt(curl, CURLOPT_HEADER, 1L);

Expand Down

0 comments on commit 74b87a8

Please sign in to comment.