Skip to content

Commit

Permalink
mime: do not perform more than one read in a row
Browse files Browse the repository at this point in the history
Input buffer filling may delay the data sending if data reads are slow.
To overcome this problem, file and callback data reads do not accumulate
in buffer anymore. All other data (memory data and mime framing) are
considered as fast and still concatenated in buffer.
As this may highly impact performance in terms of data overhead, an early
end of part data check is added to spare a read call.
When encoding a part's data, an encoder may require more bytes than made
available by a single read. In this case, the above rule does not apply
and reads are performed until the encoder is able to deliver some data.

Tests 643, 644, 645, 650 and 654 have been adapted to the output data
changes, with test data size reduced to avoid the boredom of long lists of
1-byte chunks in verification data.
New test 664 checks mimepost using single-byte read callback with encoder.
New test 665 checks the end of part data early detection.

Fixes curl#4826
Reported-by: MrdUkk on github
  • Loading branch information
monnerat committed Mar 3, 2020
1 parent e3a5c1f commit 304c41a
Show file tree
Hide file tree
Showing 15 changed files with 679 additions and 77 deletions.
196 changes: 147 additions & 49 deletions lib/mime.c

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lib/mime.h
Expand Up @@ -31,6 +31,7 @@
/* Part flags. */
#define MIME_USERHEADERS_OWNER (1 << 0)
#define MIME_BODY_ONLY (1 << 1)
#define MIME_FAST_READ (1 << 2)

#define FILE_CONTENTTYPE_DEFAULT "application/octet-stream"
#define MULTIPART_CONTENTTYPE_DEFAULT "multipart/mixed"
Expand Down Expand Up @@ -87,7 +88,7 @@ typedef struct {
typedef struct {
enum mimestate state; /* Current state token. */
void *ptr; /* State-dependent pointer. */
size_t offset; /* State-dependent offset. */
curl_off_t offset; /* State-dependent offset. */
} mime_state;

/* minimum buffer size for the boundary string */
Expand Down
2 changes: 1 addition & 1 deletion tests/data/Makefile.inc
Expand Up @@ -85,7 +85,7 @@ test626 test627 test628 test629 test630 test631 test632 test633 test634 \
test635 test636 test637 test638 test639 test640 test641 test642 \
test643 test644 test645 test646 test647 test648 test649 test650 test651 \
test652 test653 test654 test655 test656 test658 test659 test660 test661 \
test662 test663 \
test662 test663 test664 test665 \
test670 test671 test672 test673 \
\
test700 test701 test702 test703 test704 test705 test706 test707 test708 \
Expand Down
12 changes: 6 additions & 6 deletions tests/data/test643
Expand Up @@ -69,18 +69,18 @@ s/boundary=------------------------[a-z0-9]*/boundary=--------------------------
POST /643 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Content-Length: 718
Content-Length: 640
Content-Type: multipart/form-data; boundary=----------------------------

------------------------------
Content-Disposition: form-data; name="sendfile"; filename="postit2.c"

this is what we post to the silly web server
dummy

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

this is what we post to the silly web server
dummy

------------------------------
Content-Disposition: form-data; name="filename"
Expand All @@ -99,18 +99,18 @@ blah blah
POST /643 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Content-Length: 732
Content-Length: 654
Content-Type: multipart/form-data; boundary=----------------------------

------------------------------
Content-Disposition: form-data; name="sendfile alternative"; filename="file name 2"

this is what we post to the silly web server
dummy

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

this is what we post to the silly web server
dummy

------------------------------
Content-Disposition: form-data; name="filename"
Expand Down
2 changes: 1 addition & 1 deletion tests/data/test644
Expand Up @@ -44,7 +44,7 @@ s/boundary=------------------------[a-z0-9]*/boundary=--------------------------
POST /644 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Content-Length: 718
Content-Length: 640
Content-Type: multipart/form-data; boundary=----------------------------

------------------------------
Expand Down
64 changes: 58 additions & 6 deletions tests/data/test645
Expand Up @@ -73,16 +73,42 @@ Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------
Expect: 100-continue

2ce
76
------------------------------
Content-Disposition: form-data; name="sendfile"; filename="postit2.c"

this is what we post to the silly web server
d
1
u
1
m
1
m
1
y
1


65

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

this is what we post to the silly web server

1
d
1
u
1
m
1
m
1
y
1


19a

------------------------------
Content-Disposition: form-data; name="filename"
Expand All @@ -108,16 +134,42 @@ Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------
Expect: 100-continue

2dc
84
------------------------------
Content-Disposition: form-data; name="sendfile alternative"; filename="file name 2"

this is what we post to the silly web server
d
1
u
1
m
1
m
1
y
1


65

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

this is what we post to the silly web server

1
d
1
u
1
m
1
m
1
y
1


19a

------------------------------
Content-Disposition: form-data; name="filename"
Expand Down
12 changes: 11 additions & 1 deletion tests/data/test650
Expand Up @@ -63,7 +63,7 @@ Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------
Expect: 100-continue

60a
361
------------------------------
Content-Disposition: form-data; name="fieldname"
Content-Type: text/plain
Expand All @@ -89,19 +89,25 @@ This is data from a file.
Content-Disposition: attachment; filename="test650.filedata"
Content-Type: text/whatever


a5
This is data from a file.

------------------------------
Content-Disposition: attachment; filename="test650.filedata"
Content-Type: text/whatever


af
This is data from a file.

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

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


10f
This is data from a file.

------------------------------
Expand All @@ -112,8 +118,12 @@ Content-Disposition: form-data; name="formlength"
Content-Disposition: form-data; name="standardinput"
Content-Type: application/octet-stream


16
Some data from stdin

30

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

0
Expand Down
18 changes: 16 additions & 2 deletions tests/data/test654
Expand Up @@ -81,7 +81,7 @@ Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------
Expect: 100-continue

20c
1af
------------------------------
Content-Disposition: form-data; name="greeting"
Content-Type: application/X-Greeting
Expand All @@ -98,7 +98,21 @@ This is data from a file
------------------------------
Content-Disposition: form-data

this is what we post to the silly web server

1
d
1
u
1
m
1
m
1
y
1


30

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

Expand Down
85 changes: 85 additions & 0 deletions tests/data/test664
@@ -0,0 +1,85 @@
<testcase>
<info>
<keywords>
HTTP
HTTP POST
HTTP MIME POST
</keywords>
</info>

#
# Server-side
<reply>
<data>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Connection: close
Content-Type: text/html

hello
</data>
<datacheck>
HTTP/1.1 200 OK
Date: Thu, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Connection: close
Content-Type: text/html

hello
</datacheck>
</reply>

# Client-side
<client>
<server>
http
</server>
# tool is what to use instead of 'curl'
<tool>
lib664
</tool>

<name>
HTTP chunked mimepost using single-byte read callback with encoder
</name>
<command>
http://%HOSTIP:%HTTPPORT/664
</command>
</client>

#
# Verify data after the test has been "shot"
<verify>
<strippart>
s/^--------------------------[a-z0-9]*/------------------------------/
s/boundary=------------------------[a-z0-9]*/boundary=----------------------------/
</strippart>
# Note that the stripping above removes 12 bytes from every occurrence of the
# boundary string and since 5 of them are in the body contents, we see
# (5*12) == 60 bytes less
<protocol>
POST /664 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------
Expect: 100-continue

7f
------------------------------
Content-Disposition: form-data; name="field"
Content-Transfer-Encoding: base64


4
ZHVt
34
bXk=
--------------------------------

0

</protocol>
</verify>
</testcase>

0 comments on commit 304c41a

Please sign in to comment.